CDOJ UESTC 1220 The Battle of Guandu
The 2015 China Collegiate Programming Contest
2015第一届中国大学生程序设计竞赛 F题
本质就是求单源最短路!注意会爆int
对于每一个村庄i,其实就是花费c[i],把一个人从y[i]转移到x[i];
如果一张图中,不存在w[i]==2的节点,那么花费肯定是0。
所以,花费就出在w[i]==2的节点上,怎么处理这些节点呢?
可以从w[i]==0的节点上流出一些人,流到w[i]==2的节点上,并且对于每个w[i]==2的节点只需要一个人。
因此,设立一个节点S,连向所有w[i]==0的节点,费用为0;
对于每一个村庄i,从y[i]到x[i]连边,费用为c[i];
然后从S出发,跑单源最短路,最终把S到w[i]==2的节点的最短路都加起来就是答案。
如果有一个w[i]==2的节点不能到达,那么就输出-1.
#include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<vector> #include<algorithm> using namespace std; const long long INF=10000000000; const int maxn=300000+10; int T,N,M; int x[maxn],y[maxn]; long long c[maxn]; int w[maxn]; vector<int>G[maxn]; queue<int>Q; bool flag[maxn]; long long dis[maxn]; struct Edge { int from,to; long long cost; } e[maxn]; int tot; void init() { for(int i=0; i<maxn; i++) G[i].clear(); if(!Q.empty()) Q.pop(); for(int i=0; i<maxn; i++) dis[i]=INF; memset(flag,0,sizeof flag); } void add(int x,int y,long long c) { tot++; e[tot].from=x; e[tot].to=y; e[tot].cost=c; G[x].push_back(tot); } void read() { tot=0; init(); scanf("%d%d",&N,&M); for(int i=1; i<=N; i++) scanf("%d",&x[i]); for(int i=1; i<=N; i++) scanf("%d",&y[i]); for(int i=1; i<=N; i++) scanf("%lld",&c[i]); for(int i=1; i<=M; i++) scanf("%d",&w[i]); for(int i=1; i<=N; i++) add(y[i],x[i],c[i]); for(int i=1; i<=M; i++) { if(w[i]==0) add(0,i,0); else if(w[i]==2) add(i,M+1,0); } } void spfa() { flag[0]=1; Q.push(0); dis[0]=0; while(!Q.empty()) { int h=Q.front(); Q.pop(); flag[h]=0; for(int i=0; i<G[h].size(); i++) { int id=G[h][i]; if(dis[h]+e[id].cost<dis[e[id].to]) { dis[e[id].to]=dis[h]+e[id].cost; if(!flag[e[id].to]) { flag[e[id].to]=1; Q.push(e[id].to); } } } } } int main() { scanf("%d",&T); for(int Case=1; Case<=T; Case++) { read(); spfa(); long long ans=0; for(int i=1; i<=M; i++) { if(w[i]==2) { if(dis[i]==INF) { ans=-1; break; } ans=ans+dis[i]; } } printf("Case #%d: %lld\n",Case,ans); } return 0; }