题意:有n个点,m条有向边,有一个人要在T时间内从1走到n,并在途中进行交易,获得尽量多的钱。这个人能带B包盐,除了1和n,每个点都有不同的盐价,所以可以根据差价赚钱。每条边有相应的时间和花费,到达一个点时有三个选择:买一包盐、卖一包盐、什么也不做,进行交易不需要时间。另外,还有K个平行世界(编号从0~K-1),每个是平行世界相应的盐价不同,但是相应道路的花费相同,这个人每次可以从第i个平行世界跳转到第(i+1)%K的平行世界(跳转前后位置相同),这个过程需要花费1分钟,但是他不能在1和n跳转,也就是说在1和n时必须是在第0个平行世界。当他到n的时候立即停止旅行,最后问能获得的最多的钱。
思路:用一个四维的数组记录在第i个城市,第t秒,在第k维空间中,有b包盐时的最大价值。可以看出,每次行动时间都是增加的,接下来可以考虑用bfs的思想,把每个节点按时间去处理,先处理时间早的,再处理时间晚的,每次行动分为到下一个城市或者进行穿越,这两个行动都可以选择是否在当地进行交易,然后去更新新的状态的值,然后插入队列。由于要按时间的顺序进行搜索,所以这里我用了优先队列去搞,需要注意的是,到达n点后是不能再进行扩展的,因此到这里要跳过(在这里wa了好多次),这样下来每个状态只进队一次,如果总的状态是x的话,复杂度就是O(xlog(x))。在搜的时候判断一下能不能到达终点,如果能到达,最后遍历一遍数组就能找到n点时的最大价值了。感觉我代码写得好丑Orz……
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<cmath> #include<vector> #include<queue> using namespace std; typedef long long ll; const int maxn=100+10; const int maxm=200+10; struct Edge { int v,tm,cost; int next; }; Edge edges[maxm<<1]; int head[maxn],nEdge=-1; int n,m,B,K,R,T; int d[maxn][210][6][6]; bool inq[maxn][210][6][6]; int prices[6][maxn]; void AddEdge(int u,int v,int tm,int cost) { nEdge++; edges[nEdge].v=v; edges[nEdge].tm=tm; edges[nEdge].cost=cost; edges[nEdge].next=head[u]; head[u]=nEdge; } struct Node { int u,times,k,b; bool operator < (const Node &a) const { return times>a.times; } }; int bfs() { memset(d,0xff,sizeof(d)); memset(inq,0,sizeof(inq)); d[1][0][0][0]=R; Node node,tmp; priority_queue<Node>q; node.u=1;node.times=0; node.k=0;node.b=0; inq[1][0][0][0]=true; q.push(node); bool flag=false; while(!q.empty()) { node=q.top();q.pop(); if(node.times>T) break; int u=node.u; if(u==n) continue; //到下一个城市 for(int i=head[u];i!=-1;i=edges[i].next) { int v=edges[i].v; int cost,tim; cost=d[u][node.times][node.k][node.b]-edges[i].cost; tim=node.times+edges[i].tm; if(tim>T||cost<0) continue; if(v==n&&node.k!=0) continue; if(v==n) flag=true; tmp.u=v;tmp.times=tim; tmp.k=node.k; if(u!=1&&u!=n) { //买一包盐 if(node.b+1<=B&&cost-prices[node.k][u]>d[v][tim][node.k][node.b+1]) { d[v][tim][node.k][node.b+1]=cost-prices[node.k][u]; tmp.b=node.b+1; if(!inq[tmp.u][tmp.times][tmp.k][tmp.b]) {q.push(tmp);inq[tmp.u][tmp.times][tmp.k][tmp.b]=true;} } //卖一包盐 if(node.b>0&&cost+prices[node.k][u]>d[v][tim][node.k][node.b-1]) { d[v][tim][node.k][node.b-1]=cost+prices[node.k][u]; tmp.b=node.b-1; if(!inq[tmp.u][tmp.times][tmp.k][tmp.b]) {q.push(tmp);inq[tmp.u][tmp.times][tmp.k][tmp.b]=true;} } } //不交易 if(cost>d[v][tim][node.k][node.b]) { d[v][tim][node.k][node.b]=cost; tmp.b=node.b; if(!inq[tmp.u][tmp.times][tmp.k][tmp.b]) {q.push(tmp);inq[tmp.u][tmp.times][tmp.k][tmp.b]=true;} } } //穿越 if(u!=1&&u!=n) { int cost=d[u][node.times][node.k][node.b]; tmp.u=u;tmp.k=(node.k+1)%K; tmp.times=node.times+1; if(tmp.times>T) continue; //买一包盐 if(node.b+1<=B&&cost-prices[node.k][u]>d[u][tmp.times][tmp.k][node.b+1]) { d[u][tmp.times][tmp.k][node.b+1]=cost-prices[node.k][u]; tmp.b=node.b+1; if(!inq[tmp.u][tmp.times][tmp.k][tmp.b]) {q.push(tmp);inq[tmp.u][tmp.times][tmp.k][tmp.b]=true;} } //卖一包盐 if(node.b>0&&cost+prices[node.k][u]>d[u][tmp.times][tmp.k][node.b-1]) { d[u][tmp.times][tmp.k][node.b-1]=cost+prices[node.k][u]; tmp.b=node.b-1; if(!inq[tmp.u][tmp.times][tmp.k][tmp.b]) {q.push(tmp);inq[tmp.u][tmp.times][tmp.k][tmp.b]=true;} } //不交易 tmp.b=node.b; if(cost>d[u][tmp.times][tmp.k][tmp.b]) { d[u][tmp.times][tmp.k][tmp.b]=cost; if(!inq[tmp.u][tmp.times][tmp.k][tmp.b]) {q.push(tmp);inq[tmp.u][tmp.times][tmp.k][tmp.b]=true;} } } } if(!flag) return -1; int ans=0; for(int i=0;i<=T;++i) for(int j=0;j<=B;++j) ans=max(ans,d[n][i][0][j]); return ans; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int t,tcase=0; scanf("%d",&t); while(t--) { tcase++; scanf("%d%d%d%d%d%d",&n,&m,&B,&K,&R,&T); memset(head,0xff,sizeof(head)); nEdge=-1; int u,v,tm,cost; for(int i=0;i<K;++i) for(int j=1;j<=n;++j) scanf("%d",&prices[i][j]); for(int i=0;i<m;++i) { scanf("%d%d%d%d",&u,&v,&tm,&cost); AddEdge(u,v,tm,cost); } int ans=bfs(); printf("Case #%d: ",tcase); if(ans!=-1) printf("%d\n",ans); else printf("Forever Alone\n"); } return 0; }