Description
Input
Output
Sample Input
4 5 1 2 1 2 3 1 3 4 1 1 3 2 2 4 2
Sample Output
6
这个题因为还会返回所以需要建双向边,还会有重边问题,但是因为返回的路不可以和正向的路相同,所以建权值为w,容量为1的边,但是源点到1和n到汇点需要建容量为2,权值为0的边
#include <iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#define oo 1<<28
#include<queue>
using namespace std;
int pre[2100];
int dis[2100];
int vist[2100];
int head[2100];
struct node
{
int u;
int v;//与u点相连的点
int f;//容量
int w;//权值
int next;//下一条边
}edge[110000];
int cnt=0;
int s,t;
void add(int u,int v,int f,int w)
{
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].f=f;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
edge[cnt].u=v;
edge[cnt].v=u;
edge[cnt].f=0;
edge[cnt].w=-w;//反向的权值为正向的相反数
edge[cnt].next=head[v];
head[v]=cnt++;
}
void init()
{
memset(pre,-1,sizeof(pre));
memset(vist,0,sizeof(vist));
for(int i=0;i<=t;i++)
{
dis[i]=oo;
}
}
int spfa()
{
int i;
init();
queue<int>q;
dis[s]=0;
vist[s]=1;
q.push(s);
while(!q.empty())//将相邻点进行松弛,直到队列为空
{
int u=q.front();//取出队头
q.pop();
i=head[u];
vist[u]=0;
while(i!=-1)
{
int w=edge[i].w;
int v=edge[i].v;
if(edge[i].f>0&&dis[v]>dis[u]+w)//判断是否可以更新
{
dis[v]=dis[u]+w;//改进s到v点的值
pre[v]=i;//记录此点的前驱
if(!vist[v])//由于距离变小了,如果v点松弛成功且v点不在队列里,因为v点有可能还能改进别的点
{
vist[v]=1;
q.push(v);
}
}
i=edge[i].next;
}
}
if(pre[t]==-1)//如果不在最短路中代表寻找失败
return 0;
return 1;
}
int Cost()
{
int ans=0;
while(spfa())//如果增广路寻找成功
{
int maxx=oo;
int p=pre[t];//初始化P指针
while(p!=-1)
{
maxx=min(edge[p].f,maxx);//求出此增广路上边的最小值
p=pre[edge[p].u];
}
p=pre[t];
while(p!=-1)
{
edge[p].f-=maxx;//正向减去
edge[p^1].f+=maxx;//反向增加
ans+=maxx*edge[p].w;//因为以单位计费,所以应当乘上流量
p=pre[edge[p].u];
}
}
return ans;
}
int main()
{
int n,m,d;
int i,j;
while(~scanf("%d%d",&n,&m))
{
int u,v,w;
memset(head,-1,sizeof(head));
for(i=0;i<m;i++)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,1,w);
add(v,u,1,w);//之前没有看清楚,看了一下POJ别人给的注意发现是无向图
}
add(0,1,2,0);
add(n,n+1,2,0);//因为从源点到汇点和汇点到源点要走两遍,所以容量为2
s=0;t=n+1;
printf("%d\n",Cost());
}
return 0;
}