poj2135 Farm Tour

链接:http://poj.org/problem?id=2135

 

做题思路出处:http://www.cnblogs.com/rainydays/archive/2012/07/05/2577386.html

题意:给定一个无向图,要从1点到n点再返回1点,每条边最多走一次,问最短需要走多远。

分析:最小费用最大流,把题意看成是要找两条无交集的从1到n的路线,使距离和最小。图中的点和边就是网络流图中的点和边。设置一个源,接到1点,设置一个汇,从n点接到汇。为保证无交集,我们把每条边的流量设置为1,而源发出的流量和汇接收的流量均为2。每条边的费用就是该边在原图中的权值。

注意:有重边,所以要用邻接表。因为是无向图,所以要在加边时,原图中的一条边要变成网络流图中的两条边(如果把反向负权费用边也算上就总共4条边)。

由于最短路算法是最小费用最大流算法的子算法,所以有些最短路的题可能要用到最小费用最大流。

 

我觉得构图的两个地方很关键,一个就是对无向图的诠释。还有一个就是在容量的设定上:源点和汇点的连边都设置为2,其它都为1。虽然分析指出了这样做的原因。但可能自己来想还是有点捉急。

这样求得两条无交集的路线的最大流看来一定是2,题目中也说明必定会有来回的路,且不走相同的路。具体整个过程怎么实现的,还是有点模模糊糊。就觉得大概是这样。。。。好吧。代码就没什么好说的了,套一下MCMF的模板就可以了。关键是构图啊,构图!

 

#include<cstdio>
#include<cstring>
#include<queue>
#define MAXE 40005
#define MAXN 1100
#define INF 0x7fffffff
#define MIN(a,b) a>b?b:a
using namespace std;

int n,m;
int st,ed,cnt;
int mincost;
int head[MAXN],dist[MAXN],vist[MAXN],pre[MAXN],pos[MAXN];
struct Edge
{
   int to;
   int cap;
   int cost;
   int next;
}edge[MAXE];


void add(int u,int v,int cap,int cost)
{
   edge[cnt].to=v;
   edge[cnt].cap=cap;
   edge[cnt].cost=cost;
   edge[cnt].next=head[u];
   head[u]=cnt++;
   edge[cnt].to=u;
   edge[cnt].cap=0;
   edge[cnt].cost=-cost;
   edge[cnt].next=head[v];
   head[v]=cnt++;
}

void MCMF(int st,int ed)
{
   int i,u,v;
   int aug;
   mincost=0;
   for(;;)
   {
       memset(vist,0,sizeof(vist));
	   memset(pre,-1,sizeof(pre));
	   for(i=0;i<=ed;i++)
		   dist[i]=INF;
	   dist[st]=0;
	   pre[st]=st;
	   vist[st]=1;
	   queue<int> q;
	   q.push(st);
	   while(!q.empty())
	   {
	      u=q.front();
		  q.pop();
		  vist[u]=0;
		  for(i=head[u];i!=-1;i=edge[i].next)
		  {
		     v=edge[i].to;
			 if(edge[i].cap>0&&dist[v]>dist[u]+edge[i].cost)
			 {
			     dist[v]=dist[u]+edge[i].cost;
				 pre[v]=u;
				 pos[v]=i;
				 if(!vist[v])
				 {
					vist[v]=1;
				    q.push(v);
				 }
			 }
		  }
	   }
	   if(pre[ed]==-1)
		   break;
	   aug=INF;
	   for(u=ed;u!=st;u=pre[u])
		   aug=MIN(aug,edge[pos[u]].cap);
	   mincost+=dist[ed]*aug;
	   for(u=ed;u!=st;u=pre[u])
	   {
	      edge[pos[u]].cap-=aug;
		  edge[pos[u]^1].cap+=aug;
	   }
   }
   return ;
}

int main()
{
	int i,a,b,c;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		st=0;
		ed=n+1;
		cnt=0;
		memset(head,-1,sizeof(head));
	   for(i=0;i<m;i++)
	   {
	      scanf("%d%d%d",&a,&b,&c);
		  add(a,b,1,c);
		  add(b,a,1,c);
	   }
	   add(st,1,2,0);  //源点和汇点的连边容量设置为2
	   add(n,ed,2,0);
	   MCMF(st,ed);
	   printf("%d\n",mincost);
	}
   return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值