题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1747
题面PDF
题目大意:
从1点到N点走2次,两次的路径要求不能经过任意一条重边,求总代价最小,若不能则输出”Back to jail“。
解题:
很容易以为是最短路,走两遍最短路,第一遍过程中记录下最短路上的边,删去。网上很多题解都说明显是错的,但似乎没有给出证明。个人认为应该是,当同时存在两条以上最短路时,任选一条,会造成错误,若最短路唯一,这样做应该是对的。
代码:
#include <iostream> #include <cstdio> #include <cstring> #define inf 0x3f3f3f3f using namespace std; int mapp[105][105],cnt; int dist[105],path[105]; bool vis[105]; struct edge { int aft,bef; }store[10010]; void init(int n) { cnt=0; memset(vis,0,sizeof(vis)); memset(mapp,inf,sizeof(mapp)); memset(dist,inf,sizeof(dist)); memset(path,0,sizeof(path)); for(int i=1;i<=n;i++) mapp[i][i]=0; } void dijkstra(int s,int n) { int tmp,minn,p; vis[s]=1; dist[s]=0; for(int i=1;i<=n;i++) { tmp=mapp[s][i]; if(s!=i&&(tmp<inf)) { cnt++; store[cnt].aft=i; store[cnt].bef=s; path[i]=cnt; dist[i]=tmp; } } while(1) { minn=inf; for(int i=1;i<=n;i++) { if(!vis[i]&&dist[i]<minn) { minn=dist[i]; p=i; } } if(minn==inf)break; vis[p]=1; for(int i=1;i<=n;i++) { tmp=dist[p]+mapp[p][i]; if(tmp<dist[i]) { dist[i]=tmp; cnt++; store[cnt].bef=p; store[cnt].aft=i; path[i]=cnt; } } } } void update(int n) { int bef,x,a,b,v; bef=n; while(1) { x=path[bef]; a=store[x].aft; b=store[x].bef; mapp[a][b]=inf; mapp[b][a]=inf; bef=b; if(bef==1)break; } } int main() { int n,m,a,b,v,ans1,ans; while(scanf("%d",&n)) { if(n==0)break; scanf("%d",&m); init(n); for(int i=0;i<m;i++) { scanf("%d%d%d",&a,&b,&v); mapp[a][b]=mapp[b][a]=v; } dijkstra(1,n); if(dist[n]==inf) printf("Back to jail\n"); else { update(n); ans1=dist[n]; cnt=0; memset(vis,0,sizeof(vis)); memset(dist,inf,sizeof(dist)); memset(path,0,sizeof(path)); for(int i=1;i<=n;i++) mapp[i][i]=0; dijkstra(1,n); if(dist[n]==inf) printf("Back to jail\n"); else { ans=dist[n]+ans1; printf("%d\n",ans); } } } return 0; }
正解:
其实是非常标准的费用流,源点到1为容量为2,代价为0,,N到汇点容量为2,代价为0。然后每条边容量为1,代价即为其代价,跑一遍费用流即可。
代码:
#include <iostream> #include <cstring> #include <cstdio> #include <queue> using namespace std; int sumFlow; const int MAXN = 105; const int MAXM = 1000200; const int INF = 1000000000; struct Edge { int u; int v; int cap; int cost; int next; }edge[MAXM<<2]; int NE; int head[MAXN], dist[MAXN], pp[MAXN]; bool vis[MAXN]; void init() { NE=0; memset(head,-1,sizeof(head)); } void addedge(int u,int v,int cap,int cost) { edge[NE].u=u;edge[NE].v=v;edge[NE].cap=cap;edge[NE].cost=cost; edge[NE].next=head[u];head[u]=NE++; edge[NE].u=v;edge[NE].v=u;edge[NE].cap=0;edge[NE].cost=-cost; edge[NE].next=head[v];head[v]=NE++; } bool SPFA(int s,int t,int n) { int i,u,v; queue<int>qu; memset(vis,false,sizeof(vis)); memset(pp,-1,sizeof(pp)); for(i=0;i<=n;i++) dist[i]=INF; vis[s]=true; dist[s]=0; qu.push(s); while(!qu.empty()) { u=qu.front(); qu.pop(); vis[u]=false; for(i=head[u];i!=-1;i=edge[i].next) { v=edge[i].v; if(edge[i].cap&&dist[v]>dist[u]+edge[i].cost) { dist[v]=dist[u]+edge[i].cost; pp[v]=i; if(!vis[v]) { qu.push(v); vis[v]=true; } } } } if(dist[t]==INF) return false; return true; } int MCMF(int s,int t,int n) { int flow=0; // 总流量 int i,minflow,mincost; mincost=0; while(SPFA(s,t,n)) { minflow=INF+1; for(i=pp[t];i!=-1;i=pp[edge[i].u]) if(edge[i].cap<minflow) minflow=edge[i].cap; flow+=minflow; for(i=pp[t];i!=-1;i=pp[edge[i].u]) { edge[i].cap-=minflow; edge[i^1].cap+=minflow; } mincost+=dist[t]*minflow; } sumFlow=flow; // 最大流 if(sumFlow<2) return -1; else return mincost; } int main() { int n,m; int u,v,c; while(scanf("%d",&n)) { if(n==0)break; scanf("%d",&m); init(); int S=0; int T=n+1; for(int i=0;i<m;i++) { scanf("%d%d%d",&u,&v,&c); addedge(u,v,1,c); addedge(v,u,1,c); } addedge(S,1,2,0); addedge(n,T,2,0); int ans=MCMF(S,T,T+1); if(ans==-1) printf("Back to jail\n"); else printf("%d\n",ans); } return 0; }