题意:有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;
}