题干:

  Coach Pang loves his boyfriend Uncle Yang very much. Today is Uncle Yang’s birthday, Coach Pang wants to have a romantic candlelit dinner at Uncle Yang’s house and he has to arrive there in T minutes. 
  There are N houses in their city numbered from 1 to N. Coach Pang lives in house 1 while Uncle Yang lives in house N. The houses are connected byM directed roads. It takes some time and usually a fee to pass one road. Coach Pang wants to reach Uncle Yang’s house before the dinner starts with as much money as possible. 
  But the matter is not so simple. Coach Pang decides to do some salt trade on the way to Uncle Yang’s house. The host of each house offers a price of a bag of salt, so Coach Pang can make a profit from the price differences. Each time when Coach Pang arrives at a house (except the house 1 and the house N). He can buy one bag of salt, sell one bag of salt or do nothing. Coach Pang can carry at most B bags of salt with him, and he carries no salt when he leaves his house. The trading is so efficient that the time cost of trading can be ignored. 
  However, the problem is more complicated than imagine. Coach Pang has a handheld device that can perform a journey around K parallel universes numbered from 0 to K-1. Coach Pang lives in the universe 0. When Coach Pang uses the device in universe i, he will be transported to the same place and the same time of universe (i+1) modK. The host of the house at the same place in different universe may offer a different price of salt. Luckily, the time cost and fee of the city roads are uniform among the K universes. The journey between universes costs no time but Coach Pang has to stand still watching the ads on the device for one minute every time before the device works. Remember, Coach Pang should never visit house 1 or house N in a universe other than universe 0, because the situation might become uncontrollable if he bumps into himself or his boyfriend in another universe. 
  The time is running out. Coach Pang asks you to tell him whether he can arrive at Uncle Yang’s house in time, and how much money Coach Pang can have at most when the dinner starts. Coach Pang has R yuan at the start, and will end his journey immediately once he arrives at Uncle Yang’s house. He must arrive at Uncle Yang’s house in T minutes, and he can’t have negative amount of money anywhere anytime. Please help him!

Input

  The first line of the input is an integer C representing the number of test cases. 
  For each test case, the first line will contain 6 integers N, M, B, K, R, T, as described above. 
  (2 <= N <= 100, 0 <= M <= 200, 1 <= B <= 4, 2 <= K <= 5, 0 <= R <= 10 5, 0 <= T <= 200) 
  The following K lines contain N integers each, indicating the price p ij (0 <= i < K, 1 <= j <= N) for a bag of salt offered by the host of house j in the universe i. The price of house 1 and house N will be marked as -1.(1 <= p ij <= 100) 
  Then M lines follow, each contains 4 integers a, b, t and m, indicating that there is a road from house a to house b that costs t minutes of time and m yuan of money. (1 <= a,b <= N, a<> b, 1 <= t <=15, 0 <= m <= 100) 

Output

  For each test case, output one line containing “Case #x: y”, where x is the case number (starting from 1) and y is the most money Coach Pang can have if he can have dinner with Uncle Yang on time. 
  Print "Forever Alone" otherwise.

Sample Input

2
3 2 1 2 10 6
-1 1 -1
-1 5 -1
1 2 1 0
2 3 1 1
2 2 1 2 5 5
-1 -1
-1 -1
1 2 10 2
1 2 2 10

Sample Output

Case #1: 17
Case #2: Forever Alone

题目大意:

给定输入:N个点, M条单向边, 最多携带B袋食盐, K个平行宇宙, 最初有R元钱, 要求在T时间内到达。然后输入K行每行n个数代表当前宇宙当前城市的食盐价格。然后输入M行道路信息a, b, t ,m,代表a->b的单向边,耗费时间t,过路费m。

给定一张图,有N个城市M条单向道路,每条道路有须要消耗的时间t以及过路费m,一个人要在T分钟的时间内,从自己的城市(下标为1)赶到他男朋友的城市(下标为n),这个人最初有R元,为了在到男朋友家的同时赚更多的钱,在路途中还可以顺路做食盐生意,每个城市有一个食盐价格(不论是买还是卖都是这个价格)。起初身上没有食盐,某一个时刻身上最多带B袋盐。每到达一个有三种操作能够选择:1.售出一袋食盐、2.购买一袋食盐、3.什么都不做。(这三种操作的时间忽略不计)。同时,还存在K个平行宇宙,在一个城市能够选择穿越平行宇宙到达另一个宇宙的这个城市,花费时间为1,不同宇宙的同一城市食盐价格不同,可是不同宇宙同一道路的过路费和消耗的时间是相同的,题目还有一个限制条件:这个人不能在别的宇宙回到自己家或者男朋友家,求最后是否能到达他男朋友家以及最多能有多少钱。

解题报告:

不同于一般的记忆化bfs,这题的 时间 要素既是优先队列的key,又是dp中的一个维度。

这是因为你最终要求的是最大钱数,所以dp代表的含义就应该是钱数,而时间这个要素又不能忽略,所以就放到状态中去了。

dp[pos][t][b][k]代表当前在pos号点,t时刻,携带了b袋盐,在第k个平行宇宙时的最大钱数。算一下总状态数100*200*4*5很小,所以可以直接bfs。

注意代码中那个入队的限制:仅在第一次更新的时候入队,是为了优化时间,不加那个if判断而是直接push进去的话也可以过。

一般的bfs中都是加一个bool类型的vis数组,当数组值为0的时候才push,这里的dp值=-1的时候才push是一个道理。但是这里是否可以这样呢?一般的时候都是一个状态然后代表到达这个状态的最短时间,所以随着时间的推移这个状态肯定不会被遍历到了,或者遍历到的时候可以直接continue,因为要求的是最短时间,所以可以直接状态只入队一次,因为只可能更新一次。

但是这个题,明明可能更新这个状态很多次,为什么还是可以直接=-1的时候就入队,再往后的更新都不需要入队了呢?因为我们把时间的维度加到状态里面去了,而pq里面又是按照时间作为关键字,所以当我取到这个状态x的时候,就意味着后面所有出现的状态都不可能再去更新状态x了,因为时间肯定是变了的,所以当前dp值就不可能再改变了,也就是说这就是个完成值了,所以只需要进队一次,作用是:来代表可以取出来用这个状态继续更新其他值。就可以了。所以队列中只需要记录这个状态一次。

再一个不同就是pq维护的Node节点信息不同,一般的是dp值在里面:比如搜索地图的时候,时间信息在里面;比如Dijkstra中,最短距离在里面。而这个题,dp值是最大金钱,而这个信息并没有在里面。

究其原因就是发现,一般的时候其实dp的值也可以不放进去,但是因为我们经常需要这个dp值去当关键字key扔到pq中,所以把dp值信息加到了key中。所以更一般来讲,Node中维护的信息一般就是dp数组的维度的元素含义就可以了,而值的含义理论上不需要放进去。

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define F first
#define S second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair<int,int> PII;
const int MAX = 2e5 + 5;
int N,M,B,K,R,T; 
int p[12][111];//p[i][j]第i+1个平行宇宙的第j号顶点的价格 p[5][101]
int dp[111][222][5][11];//dp[pos][t][b][k]
struct Edge {
	int fr,to;
	int cost,t;
	int ne;
} e[205];
int tot;
int head[MAX]; 
void add(int u,int v,int t,int cost) {
	e[++tot].fr = u;e[tot].to = v;e[tot].cost = cost;e[tot].t = t;
	e[tot].ne = head[u];
	head[u] = tot;
}
struct Node {
	int pos,t,b,k;
	Node(){}
	Node(int pos,int t,int b,int k):pos(pos),t(t),b(b),k(k){}
	operator < (const Node & b) const {
		return t > b.t;
	} 
};
void bfs() {
	priority_queue<Node>pq;
	pq.push(Node(1,0,0,0));
	dp[1][0][0][0]=R;
	while(pq.size()) {
		Node cur = pq.top();pq.pop();
		if(cur.pos == N) continue;
		int curmoney = dp[cur.pos][cur.t][cur.b][cur.k];
		for(int i = head[cur.pos]; ~i; i = e[i].ne) {
			int nowmoney = curmoney - e[i].cost;
			if(nowmoney < 0) continue;//如果金钱不够则GG
			if(cur.t + e[i].t > T) continue;//如果已经超时了则GG 
			if((e[i].to == 1 || e[i].to == N) && cur.k!=0) continue;//如果不在1号宇宙则GG。注意不能写(k!=0&&k!=K-1),因为你这层for处理的都是在同一层宇宙的操作 
			//现在说明我可以走到下一个点了
			if(nowmoney > dp[e[i].to][cur.t + e[i].t][cur.b][cur.k]) {
				if(dp[e[i].to][cur.t + e[i].t][cur.b][cur.k] == -1) pq.push(Node(e[i].to,cur.t + e[i].t,cur.b,cur.k));
				dp[e[i].to][cur.t + e[i].t][cur.b][cur.k] = nowmoney;
			} 
			
			//下面是在e[i].to这里同时进行买卖一波的话
			if(p[cur.k][e[i].to] == -1) continue; 
			//如果还能买 
			if(cur.b < B && nowmoney - p[cur.k][e[i].to] >= 0) {
				if(nowmoney - p[cur.k][e[i].to] > dp[e[i].to][cur.t + e[i].t][cur.b+1][cur.k]) {
					if(dp[e[i].to][cur.t + e[i].t][cur.b+1][cur.k] == -1) pq.push(Node(e[i].to,cur.t + e[i].t,cur.b+1,cur.k));
					dp[e[i].to][cur.t + e[i].t][cur.b+1][cur.k] = nowmoney - p[cur.k][e[i].to] ;
					
				}
			}
			if(cur.b > 0) {//不用加上面那个判断因为这里肯定是大于零的 
				if(nowmoney + p[cur.k][e[i].to] > dp[e[i].to][cur.t + e[i].t][cur.b-1][cur.k]) {
					if(dp[e[i].to][cur.t + e[i].t][cur.b-1][cur.k] == -1) pq.push(Node(e[i].to,cur.t + e[i].t,cur.b-1,cur.k));
					dp[e[i].to][cur.t + e[i].t][cur.b-1][cur.k] = nowmoney + p[cur.k][e[i].to] ;
					
				}
			}
		}
		if(cur.pos == 1 || cur.pos == N) continue;
		if(cur.t + 1 > T) continue; 
		int nowk = (cur.k + 1) % K;
		if(curmoney > dp[cur.pos][cur.t+1][cur.b][nowk]) {
			if(dp[cur.pos][cur.t+1][cur.b][nowk] == -1) pq.push(Node(cur.pos,cur.t+1,cur.b,nowk));
			dp[cur.pos][cur.t+1][cur.b][nowk] = curmoney;
			
		}
		if(cur.b < B && curmoney - p[nowk][cur.pos] >= 0) {
			if(curmoney - p[nowk][cur.pos] > dp[cur.pos][cur.t + 1][cur.b+1][nowk]) {
				if(dp[cur.pos][cur.t + 1][cur.b+1][nowk] == -1) pq.push(Node(cur.pos,cur.t + 1,cur.b+1,nowk));
				dp[cur.pos][cur.t + 1][cur.b+1][nowk] = curmoney - p[nowk][cur.pos] ;
				
			}
		}
		if(cur.b > 0) {
			if(curmoney + p[nowk][cur.pos] > dp[cur.pos][cur.t + 1][cur.b-1][nowk]) {
				if(dp[cur.pos][cur.t + 1][cur.b-1][nowk] == -1)pq.push(Node(cur.pos,cur.t + 1,cur.b-1,nowk));
				dp[cur.pos][cur.t + 1][cur.b-1][nowk] = curmoney + p[nowk][cur.pos] ;
				
			}
		}
	}
}
int main()
{
	int t,iCase=0;
	cin>>t;
	while(t--) {
		scanf("%d%d%d%d%d%d",&N, &M, &B, &K, &R, &T);
		//init
		tot=0;
		for(int i = 1; i<=N; i++) head[i] = -1;
		memset(dp,-1,sizeof dp);
		for(int k = 0; k<K; k++) {
			for(int i = 1; i<=N; i++) {
				scanf("%d",&p[k][i]);
			}
		}
		for(int a,b,t,m,i = 1; i<=M; i++) {
			scanf("%d%d%d%d",&a,&b,&t,&m);
			add(a,b,t,m);
		}
		bfs();
		int ans = -1;
		for(int i = 1; i<=T; i++) {
			if(dp[N][i][0][0] > ans) ans = dp[N][i][0][0];
		}
		printf("Case #%d: ",++iCase);
		if(ans == -1) printf("Forever Alone\n");
      	else printf("%d\n",ans);
	}
	return 0 ;
}