poj 2112

网络流之最大流。此题的关键就是建模,看了题目之后一点思路都没有,后来在网上看了解题报告后,才明白了这个过程。首先,在原图中抽象出一个新图。新图是由旧图中的每个milking machine到每个cow的最短路径组成,每条最短路径由原图中的各点间的某些路径组成,抽象为新图中的一条路径,此过程通过floyd算法(各顶点间的最短路径)实现。接下来,就是通过二分来枚举答案。通过给出一个流量的上限,给新图的路径的容量cap赋值,如果小于等于上限cap就为1,反之为0。最后就可将问题转化为新图中,从各milk machine(即源点s)到各cow(即终点t)的最大流(一个流就对应与一头牛)(其实还不完整),如果最大流>=cow的个数,说明当前方案可行,枚举下一个值,最后由二分得到最优解。其中多源多终点问题,可通过加一个超级源和超级终点来转换为一源一终最大流问题。其实,还有一个问题没有解决,就是每台milk machine最大的工作量为m,现在有个超级源就可以很完美的规划为网络流问题。通过设定超级源到每个源点的容量为m,那么由流网络中的流守恒定理可知,从超级源流到各源的上限就是个从各源流出的流的上限,也即各milk machine所能服务的cow的上限。还有一个很明显的问题就是,一个牛肯定只能接受一个流,所以设定各个终点到超级终点的路径的容量设为1。其中最大流算法用的是EK,很慢,但目前还没掌握dinic和isap,这几天要抓紧学啊。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn=250;
const int inf=1<<20;//刚开始inf定义的过大,导致求dis[i][j]时,使两个inf相加的和溢出,得到一个错的dis[i][j],以后要注意这个问题
int dis[maxn][maxn],cap[maxn][maxn],flow[maxn][maxn];
int k,c,m,t;
void floyd()
{
	int i,j,k;
	for(k=1;k<=t;k++)
	{
		for(i=1;i<=t;i++)
		{
			for(j=1;j<=t;j++)
				dis[i][j]=dis[i][j]<(dis[i][k]+dis[k][j])?dis[i][j]:(dis[i][k]+dis[k][j]);//防止溢出,所以inf不能定义的过大
		}
	}
}
void build(int limit)
{
	memset(cap,0,sizeof(cap));
	int i,j;
	for(i=1;i<=k;i++) cap[0][i]=m;
	for(i=k+1;i<=t;i++) cap[i][t+1]=1;
	for(i=1;i<=k;i++)
	{
		for(j=k+1;j<=t;j++)
			if(dis[i][j]<inf&&dis[i][j]<=limit) cap[i][j]=1;
	}
}
bool ek()
{
	queue<int> q;
	int a[maxn],p[maxn],tot=0,i;
	memset(flow,0,sizeof(flow));
	for(;;)
	{
		memset(a,0,sizeof(a));
		a[0]=inf;
		q.push(0);
		while(!q.empty())
		{
			int x=q.front();
			q.pop();
			for(i=0;i<=t+1;i++)
			{
				if(!a[i]&&(cap[x][i]-flow[x][i])>0)
				{
					p[i]=x;
					a[i]=a[x]>(cap[x][i]-flow[x][i])?(cap[x][i]-flow[x][i]):a[x];
					q.push(i);
				}
			}
		}
		if(!a[t+1]) break;
		for(i=t+1;i!=0;i=p[i])
		{
			flow[p[i]][i]+=a[t+1];
			flow[i][p[i]]-=a[t+1];
		}
		tot+=a[t+1];
	}
	if(tot<c) return false;
	else return true;
}
int binarysearch()
{
	int high=230*200+1,low=0,mid;
	while(high-low>0)
	{
		mid=low+(high-low)/2;
		build(mid);
		if(ek()) high=mid;
		else low=mid+1;
	}
	return high;
}
int main()
{
	while(scanf("%d%d%d",&k,&c,&m)!=EOF)
	{
		int i,j;
		t=k+c;
		for(i=1;i<=t;i++)
		{
			for(j=1;j<=t;j++)
			{
				scanf("%d",&dis[i][j]);
				if(!dis[i][j]&&i!=j) dis[i][j]=inf;
			}
		}
		floyd();
		int result=binarysearch();
		cout<<result<<endl;
	}
	return 0;
}


转载于:https://www.cnblogs.com/lj030/archive/2012/12/01/3002340.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值