poj 2112 Optimal Milking(二分搜索+最大流)

题目链接:http://poj.org/problem?id=2112


题目大意:

有K台挤奶机器(1...K编号)和C头奶牛(K+1...K+C编号),每台挤奶机器可以给M头奶牛挤奶;

给出一个(K+C)*(K+C)的矩阵表示机器和奶牛两两之间的距离;

要求求出把奶牛运送到机器的最大流中的奶牛路径中的最短路径;


思路:

首先对给出的矩阵用Floyd求出机器奶牛两两之间的最短距离,记录其中最大值right和最小值left,通过二分搜索的方法,构建容量网络求出最大流,最大流即为能运送的奶牛的最大数目,如果最大流等于奶牛数C,返回路径值即可。

#include<iostream>
using namespace std;

const int inf=100000000;
int s,t;
int n,m;
int cap[250][250];//容量网络
int dist[250][250];
int K,C,M;

/*BFS+标记法求范围内最大流*/
int maxflow()  
{
	int queue[250];
	int head,tail;
	int pre[250]; //结点i的前驱
    
	int minflow;
	int flow=0;
    int x,y;
	
    while(true)
    {
        memset(pre,-1,sizeof(pre));
		
        for(queue[head=tail=0]=s;head<=tail;head++)
        {
            x=queue[head];
            for(int i=0;i<=t;i++)//当汇点还没有被标记时
			{
				if (cap[x][i]>0 && pre[i]==-1)  //当结点u指向i的边存在,且i还没有标记前驱时
				{
                    pre[i]=x;//记录结点i的前驱为u
                    queue[++tail]=i;
				}
				if(pre[t]!=-1)
					break;
			}
        }
		
        if(pre[t]==-1)
			break;//BFS后汇点没有被标记,则跳出while,已经不存在增广链
		
        minflow=inf;//初始化
        for(x=pre[y=t];y!=s;)//回溯
		{
			if(cap[x][y] < minflow)
				minflow=cap[x][y];//寻找当前增广链中最小容量的边,记录其边权(容量)
			y=x;
			x=pre[y];
		}
        for(x=pre[y=t];y!=s;) //当前增广链 流量调整
		{
			cap[x][y] -= minflow;  //正向弧容量减少
			cap[y][x] += minflow;  //反向弧容量增加
			y=x;
			x=pre[y];
		}
		
        flow += minflow;  //最大流=每次寻得的增广链的调整量之和
    }
    return flow;//返回最大流
}

bool solve(int x)
{
	int i,j;

	/*Structure Graph*/
	memset(cap,0,sizeof(cap));

	for(i=1;i<=C;i++)
		cap[0][i]=1;

	for(i=K+1;i<t;i++)
	{
		for(j=1;j<=K;j++)
		{
			if(dist[i][j]<=x)
				cap[i-K][C+j]=1;
		}
	}

	for(i=1;i<=K;i++)
		cap[C+i][t]=M;

	if(maxflow()==C)
		return true;
	else
		return false;
}

int main(int i,int j,int k)
{
	while(~scanf("%d %d %d",&K,&C,&M))
	{
		t=K+C+1;
		s=0;

		/*Input*/
		for(i=1;i<t;i++)
		{
			for(j=1;j<t;j++)
			{
				scanf("%d",&dist[i][j]);
				if(dist[i][j]==0)
					dist[i][j]=inf;
			}
		}

		/*Floyd Algorithm*/
		
		for(k=1;k<t;k++)
		{
			for(i=1;i<t;i++)
			{
				for(j=1;j<t;j++)
				{
					if(dist[i][j]>dist[i][k]+dist[k][j])
						dist[i][j]=dist[i][k]+dist[k][j];
				}
			}
		}
		
		
		int left=inf;
		int right=0;
		
		for(i=1;i<=K;i++)
		{
			for(j=K+1;j<t;j++)
			{
				if(dist[i][j]!=inf && dist[i][j]>right)
					right=dist[i][j];
				if(dist[i][j]!=inf && dist[i][j]<left)
					left=dist[i][j];
			}
		}

		/*二分搜索*/

		while(left<right)
		{
			int mid=(left+right)/2;
			if(solve(mid))
				right=mid;
			else
				left=mid+1;
		}

		printf("%d\n",right);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值