题意:
FJ带朋友参观自己的农场,从自己的房子出发到barn(谷仓、畜棚或车库),再从barn返回自己的房子,要求去回不走同一条路。
建图:取超级源点,并与房子连一条边,容量为2;取barn与超级汇点间的边的容量为2,中间的建图方法如代码。
因为此题是无向图,所以建边的时候如果建两条费用都是正的边的话,退流时无法修正费用。 所以应该建4条边: 第一对: a->b cost 1 b->a -cost 0 第二对: b->a cost 1 a->b -cost 0
摘了discuss里的一句话,也就是说,两个节点之间可能有多条路,来的时候走过连接这两个节点的路了,回去的时候也可以经过这两个节点,但是路却走另一条,所以要建连四条边。
这道题可作为我的邻接表实现的最小费用最大流模板。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define INF 100000000
struct node
{
int u,v,w,f,next;
} edge[40010];
int cnt,s,T;
int head[40010],vis[40010],pre[40010],dis[40010];
void add(int u,int v,int w,int f)
{
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].f=f;
edge[cnt].next=head[u];
head[u]=cnt++;
edge[cnt].u=v;
edge[cnt].v=u;
edge[cnt].w=0;
edge[cnt].f=-f;
edge[cnt].next=head[v];
head[v]=cnt++;
}
int ISAP()
{
int i;
memset(pre,-1,sizeof(pre));
memset(vis,0,sizeof(vis));
for(i=0; i<cnt; i++)
dis[i]=INF;
dis[s]=0;
queue<int>q;
vis[s]=1;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
i=head[u];
vis[u]=0;
while(i!=-1)
{
if(edge[i].w>0&&dis[edge[i].v]>dis[u]+edge[i].f)
{
dis[edge[i].v]=dis[u]+edge[i].f;
pre[edge[i].v]=i;
if(!vis[edge[i].v])
{
vis[edge[i].v]=1;
q.push(edge[i].v);
}
}
i=edge[i].next;
}
}
if(pre[T]==-1)
return 0;
return 1;
}
int MincostMaxFlow()
{
int ans=0;
while(ISAP())
{
int maxl=INF;
int p=pre[T];
while(p!=-1)
{
maxl=min(maxl,edge[p].w);
p=pre[edge[p].u];
}
p=pre[T];
while(p!=-1)
{
edge[p].w-=maxl;
edge[p^1].w+=maxl;
ans+=maxl*edge[p].f;
p=pre[edge[p].u];
}
}
return ans;
}
int main()
{
int n,m;
int a,b,c;
int sum;
while(scanf("%d %d",&n,&m)!=EOF)
{
T=n+1;
cnt=0;
s=0;
memset(head,-1,sizeof(head));
while(m--)
{
scanf("%d %d %d",&a,&b,&c);
add(a,b,1,c);
add(b,a,1,c);
}
add(0,1,2,0);
add(n,T,2,0);
sum=0;
sum+=MincostMaxFlow();
printf("%d\n",sum);
}
return 0;
}
其实就是模版水题