若不是看了题目分类,很难想到这题和网络流挂上钩,乍一看是求2遍最短路,但是很明显求最短路可能是错误的。
题意:FJ(又是他><)想带朋友参观自己的农场,农场有n个点,m条边,要从1点到n点,再从n点走回来,且2次不能走相同的路,问最小的花费时间。
题解:费用流,最小费用即为答案
我的构图方法:定义源点和汇点,
1.所有边权值为1,费用为所花费的时间。
2.将源点与1连接边权值为2,费用0,n与汇点相连,权值为2,费用为0.
这样保证了从1到n恰好有2条边不重复的可行流,构成了一个由1到n再到1的回路。
#include <cstdio> #include <cstring> #define min(a,b) (a>b?b:a) const int maxn=1005; const int maxm=60050; const int inf=0x4fffffff; struct Edge{ int v,next,c,w; }edge[maxm]; int head[maxn],cnt; void addedge(int u,int v,int w,int c) { edge[cnt].v=v; edge[cnt].w=w; edge[cnt].c=c; edge[cnt].next=head[u]; head[u]=cnt++; edge[cnt].v=u; edge[cnt].w=0; edge[cnt].c=-c; edge[cnt].next=head[v]; head[v]=cnt++; } int dis[maxn],pre[maxn];//最小费用和前驱结点 int alpha[maxn];//标记 int que[maxn],qhead,qrear; int spfa (int s,int e)//源汇点 { //寻找费用增广,没有返回-1; for (int i=0 ; i<maxn ; ++i) dis[i]=inf; memset (alpha , 0 , sizeof(alpha)); dis[s]=0; que[qhead=0]=s; qrear=1; alpha[s]=1; while (qhead!=qrear)//头可能大于尾 { int k=que[qhead++]; qhead%=maxn;//循环队列 alpha[k]=0; for (int q=head[k] ; ~q ; q=edge[q].next) if(edge[q].w) if(dis[k]+edge[q].c<dis[edge[q].v]) { dis[edge[q].v]=dis[k]+edge[q].c; pre[edge[q].v]=q; if(!alpha[edge[q].v]) { alpha[edge[q].v]=true; if(edge[q].c<0) { qhead=(qhead-1+maxn)%maxn; que[qhead]=edge[q].v; } else { que[qrear++]=edge[q].v; qrear%=maxn; } } } } if(dis[e]==inf)return -1; //终点不可达,返回-1; int k=inf; for(int i=e ; i!=s ; i=edge[pre[i]^1].v) k=min(k,edge[pre[i]].w); //sum+=k;//sum记录最大流(有些题里会用到) return k;//返回该可行流流量 } int mcmf(int s,int t) { int ans=0,k; while (~(k=spfa(s,t))) { for (int i=t ; i!=s ; i=edge[pre[i]^1].v) { edge[pre[i]].w-=k; edge[pre[i]^1].w+=k; }//更新流 ans+=dis[t]*k;//最小费用*流量 } return ans; } int main () { int n,m; int u,v,c; while (~scanf("%d%d",&n,&m)) { memset (head , -1 , sizeof(head)); cnt=0; 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(0,1,2,0); addedge(n,n+1,2,0); int ans=mcmf(0,n+1); printf("%d\n",ans); } return 0; }
#include <cstdio> #include <cstring> #define min(a,b) (a>b?b:a) const int maxn=1005; const int maxm=60050; const int inf=0x4fffffff; struct Edge{ int v,next,c,w; }edge[maxm]; int head[maxn],cnt; void addedge(int u,int v,int w,int c) { edge[cnt].v=v; edge[cnt].w=w; edge[cnt].c=c; edge[cnt].next=head[u]; head[u]=cnt++; edge[cnt].v=u; edge[cnt].w=0; edge[cnt].c=-c; edge[cnt].next=head[v]; head[v]=cnt++; } int dis[maxn],pre[maxn];//最小费用和前驱结点 int alpha[maxn];//标记 int que[maxn],qhead,qrear; int spfa (int s,int e)//源汇点 { //寻找费用增广,没有返回-1; for (int i=0 ; i<maxn ; ++i) dis[i]=inf; memset (alpha , 0 , sizeof(alpha)); dis[s]=0; que[qhead=0]=s; qrear=1; alpha[s]=1; while (qhead!=qrear)//头可能大于尾 { int k=que[qhead++]; qhead%=maxn;//循环队列 alpha[k]=0; for (int q=head[k] ; ~q ; q=edge[q].next) if(edge[q].w && dis[k]+edge[q].c<dis[edge[q].v]) { dis[edge[q].v]=dis[k]+edge[q].c; pre[edge[q].v]=q; if(!alpha[edge[q].v]) { alpha[edge[q].v]=true; if(edge[q].c<0) { qhead=(qhead-1+maxn)%maxn; que[qhead]=edge[q].v; } else { que[qrear++]=edge[q].v; qrear%=maxn; } } } } if(dis[e]==inf)return -1; //终点不可达,返回-1; int k=inf; for(int i=e ; i!=s ; i=edge[pre[i]^1].v) k=min(k,edge[pre[i]].w); //sum+=k;//sum记录最大流(有些题里会用到) return k;//返回该可行流流量 } int mcmf(int s,int t) { int ans=0,k; while (~(k=spfa(s,t))) { for (int i=t ; i!=s ; i=edge[pre[i]^1].v) { edge[pre[i]].w-=k; edge[pre[i]^1].w+=k; }//更新流 ans+=dis[t]*k;//最小费用*流量 } return ans; } int main () { int n,m; int u,v,c; while (~scanf("%d%d",&n,&m)) { memset (head , -1 , sizeof(head)); cnt=0; 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(0,1,2,0); addedge(n,n+1,2,0); int ans=mcmf(0,n+1); printf("%d\n",ans); } return 0; }