POJ 2135 Farm Tour【最小费用最大流】
题意: 有一个有 N 个节点的无向图,要求找出两条从1 节点 到 n 节点两条最短且不同的路径,求出其长度和。
分析: 由于是两条不同的路径,可以用最小费用最大流,因为是有重边,所以要用邻接表存储边。
建图:
建立一个源点 s = 0
在 s 和 1 节点之间加一条容量为 2 费用为 0 的边,
建立一个汇点 u = n+1
在 n 和 u 之间加一条容量为 2 费用为 0 的边,
两两节点如果连通,就在之间加一条 容量为 1,费用为边的长度的双向边。
#include<stdio.h> #include<string.h> #define clr(x)memset(x,0,sizeof(x)) #define INF 0x1f1f1f1f #define min(a,b)(a)<(b)?(a):(b) #define max(a,b)(a)>(b)?(a):(b) struct node { int next,from,to,w,c; }e[100010]; int tot; int head[1010]; void add(int st,int u,int wi,int flow) { e[tot].from=st; e[tot].w=wi; e[tot].c=flow; e[tot].to=u; e[tot].next=head[st]; head[st]=tot++; } int q[100010]; int pre[1010]; int dis[1010]; int v[1010]; int s,t; int spfa() { int i,x,front,rear,k; front=rear=0; for(i=0;i<=t;i++) dis[i]=INF; clr(v); pre[s]=-1; q[rear++]=s; dis[s]=0; v[s]=1; while(front<rear) { x=q[front++]; v[x]=0; for(i=head[x];i!=-1;i=e[i].next) { k=e[i].to; if(e[i].c&&dis[x]+e[i].w<dis[k]) { dis[k]=dis[x]+e[i].w; pre[k]=i; if(!v[k]) { v[k]=1; if(dis[k]<=dis[x]&&front>0) q[--front]=k; else q[rear++]=k; } } } } if(dis[t]!=INF) return 1; return 0; } int costflow() { int tt=0; int tot=0,flow=0,u,minf=INF; while(spfa()) { for(u=pre[t];u!=-1;u=pre[e[u].from]) minf=min(minf,e[u].c); for(u=pre[t];u!=-1;u=pre[e[u].from]) { e[u].c-=minf; e[u^1].c+=minf; flow+=e[u].w*minf; } } return flow; } int main() { int m,n,a,b,c; while(scanf("%d%d",&n,&m)!=EOF) { tot=0; memset(head,-1,sizeof(head)); s=0; t=n+1; add(s,1,0,2); add(1,s,0,2); add(n,t,0,2); add(t,n,0,2); while(m--) { scanf("%d%d%d",&a,&b,&c); add(a,b,c,1); add(b,a,-c,0); add(b,a,c,1); add(a,b,-c,0); } printf("%d\n",costflow()); } return 0; }