版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a54665sdgf/article/details/80714725
题目大意:给你n个点和m条带权边,让你求出从点1到点n的两条边,使得这两条边没有公共点(除了起点和终点),并且权和最小。
思路:方法很简单,令每条边的容量为1,用最小费用最大流的方法求出流量为2时的最小费用即可。注意由于每个点的容量是没有限制的,只有边的容量有限制,所以不能保证每个点只经过一次。解决方法是把每个点拆成两个点P1和P2,在P1和P2间连一条容量为1、费用为0的边,这样就可以保证走过的点不重复了。在不同的点u、v之间连边的时候,只需要把点P2(u)与P1(v)相连即可。
AC代码:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<queue>
#define FRER() freopen("i.txt","r",stdin)
using namespace std;
const int M=2000+100,N=30000;
const int INF=0x3f3f3f3f;
int head[M],p[M],dc[M],df[M],vis[M],from[N],to[N],nxt[N],cap[N],cost[N];
int n,m,nEdge,mincost;
void addEdge(int u,int v,int cap_,int cost_)
{
nxt[nEdge]=head[u],from[nEdge]=u,to[nEdge]=v,cap[nEdge]=cap_,cost[nEdge]=cost_,head[u]=nEdge++;
nxt[nEdge]=head[v],from[nEdge]=v,to[nEdge]=u,cap[nEdge]=0,cost[nEdge]=-cost_,head[v]=nEdge++;
}
int P1(int u)
{
return (u-1)*2;
}
int P2(int u)
{
return (u-1)*2+1;
}
void BellmanFord(int s,int t)
{
memset(dc,INF,sizeof dc);
queue<int> q;
for(int k=0; k<2; ++k)
{
memset(dc,INF,sizeof dc);
memset(df,0,sizeof df);
memset(vis,0,sizeof vis);
q.push(s);
vis[s]=1;
dc[s]=0,df[s]=1;
while(!q.empty())
{
int u=q.front();
q.pop(),vis[u]=0;
for(int e=head[u]; e!=-1; e=nxt[e])
{
int v=to[e];
if(cap[e]&&dc[u]+cost[e]<min(dc[v],dc[t]))
{
dc[v]=dc[u]+cost[e];
p[v]=e,df[v]=min(df[u],cap[e]);
if(!vis[v])
vis[v]=1,q.push(v);
}
}
}
mincost+=dc[t];
for(int u=t; u!=s; u=from[p[u]])
cap[p[u]]-=df[t],cap[p[u]^1]+=df[t];
}
}
int main()
{
//FRER();
while(scanf("%d%d",&n,&m)==2)
{
nEdge=mincost=0;
memset(head,-1,sizeof head);
int u,v,c;
while(m--)
{
scanf("%d%d%d",&u,&v,&c);
addEdge(P2(u),P1(v),1,c);
}
for(int u=2; u<=n-1; ++u)
addEdge(P1(u),P2(u),1,0);
BellmanFord(P2(1),P1(n));
printf("%d\n",mincost);
}
return 0;
}