若不是看了题目分类,很难想到这题和网络流挂上钩,乍一看是求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;
}