poj 2112 floyd+最大流(所有牛挤奶走的最长路的最小值)

题意:给定K台挤奶机器和C头牛,每台挤奶机只能供M头牛挤奶。现在给出(K + C)*(K + C)的距离矩阵,表示第i个物体到第j个物体之间的路径长度,若此值为零,则说明其间不可直接到达。问怎么安排这C头牛到K台机器挤奶,使得需要走最长路程到挤奶机器的奶牛所走的路程最少,并求出这个最小值。

思路:首先利用Floyd算法求出每个奶牛到每个挤奶机的最短距离。

此后二分答案,先假定一个最大距离的的最小值 maxdist, 对每个maxdist值,都重新构图。 构图策略是:每个奶牛和挤奶器都是一个节点,添加一个源,连边到所有奶牛节点,这些边容量都是1。添加一个汇点,每个挤奶器都连边到它。这些边的容量都是M。如果奶牛节点i和挤奶器节点j之间的距离<= maxdist,则从i节点连一条边到j节点,表示奶牛i可以到挤奶器j去挤奶。该边容量为1。该图上的最大流如果是C(奶牛数),那么就说明假设的 maxdist成立,则减小 maxdist再试。

注意二分过程,设下界为low,上界为high。当最大流等于奶牛数量时,如果maxdist值等于low则返回答案,否则是high=maxdist(传统的二分查找是high=mid-1,这里相当于等于mid,不减一);当最大流小于奶牛数量时,使low=maxdist+1。

版本2用邻接表建图,且最大流算法的队列用stl实现。

内容部分参考北大郭炜老师的课件.

#include <stdio.h>
#include <string.h>
#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b
#define N 235
int n,m,thresh;
int dis[N][N],c[N][N],a[N],p[N],q[200000];
void createmap(int res){
	int i,j;
	memset(c,0,sizeof(c));
	for(i = 1;i<=m;i++)//源点到牛
		c[0][i] = 1;
	for(i = m+1;i<=n+m;i++)//机器到汇点
		c[i][n+m+1] = thresh;
	for(i = 0;i<n;i++)//牛到机器
		for(j = n;j<n+m;j++)
			if(dis[i][j] <= res)
				c[j-n+1][i+m+1] = 1;
}
int maxflow(int s,int t){//最大流
	int front,rear,i,res=0;
	while(1){
		front = rear = -1;
		memset(a,0,sizeof(a));
		memset(p,0,sizeof(p));
		a[s] = 0x3fffffff;
		q[++rear] = s;
		while(front < rear){
			int now = q[++front];
			for(i = 0;i<=t;i++)
				if(!a[i] && c[now][i]>0){
					a[i] = min(a[now],c[now][i]);
					p[i] = now;
					q[++rear] = i;
				}
		}
		if(!a[t])
			break;
		res += a[t];
		for(i = t;i!=s;i=p[i]){
			c[p[i]][i] -= a[t];
			c[i][p[i]] += a[t];
		}
	}
	return res;
}
int main(){
	freopen("a.txt","r",stdin);
	while(scanf("%d %d %d",&n,&m,&thresh)!=EOF){
		int i,j,k,res,high=0,low=0;
		for(i = 0;i<n+m;i++)
			for(j = 0;j<n+m;j++){
				scanf("%d",&dis[i][j]);
				if(dis[i][j] == 0)
					dis[i][j] = 0x3fffffff;
			}
		//floyd
		for(k = 0;k<n+m;k++)
			for(i = 0;i<n+m;i++)
				for(j = 0;j<n+m;j++)
					dis[i][j] = min(dis[i][j],dis[i][k]+dis[k][j]);
		//确定二分上界high
		for(i = 0;i<n;i++)
			for(j = n;j<n+m;j++)
				high = max(high,dis[i][j]);
		//二分答案
		while(low <= high){
			res = (low+high)/2;
			createmap(res);//每次都得重新建图
			if(m == maxflow(0,n+m+1)){
				if(res == low)
					break;
				high = res;//这是与传统二分查找不一样的地方
			}else
				low = res+1;
		}
		printf("%d\n",res);
	}
	return 0;
}

版本2:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
#include <cstdlib>
using namespace std;
#define INF 0x3fffffff
#define clc(s,t) memset(s,t,sizeof(s))
#define N 235
int m,c,n,k;
int dis[N][N];
struct edge{
    int y,next,c;
}e[N*N*2];
int first[N],top;
void addedge(int x,int y,int c){
    e[top].y = y;
    e[top].c = c;
    e[top].next = first[x];
    first[x] = top++;
}
void add(int x,int y,int c){
    addedge(x,y,c);
    addedge(y,x,0);
}
void create(int d){
    int i,j;
    clc(first,-1);
    top = 0;
    for(i = 1;i<=m;i++)
        add(0,i,k);
    for(i = m+1;i<=m+c;i++)
        add(i,n+1,1);
    for(i = 1;i<=m;i++)
        for(j = m+1;j<=m+c;j++)
            if(dis[i][j] <= d)
                add(i,j,1);
}
int maxflow(){
    queue<int> q;
    int i,now,res=0,p[N],a[N],id[N];
    while(1){
        clc(p,0);
        clc(a,0);
        a[0] = INF;
        q.push(0);
        while(!q.empty()){
            now = q.front();
            q.pop();
            for(i = first[now];i!=-1;i=e[i].next){
                if(!a[e[i].y] && e[i].c>0){
                    a[e[i].y] = min(a[now],e[i].c);
                    p[e[i].y] = now;
                    id[e[i].y] = i;
                    q.push(e[i].y);
                }
            }
        }
        if(a[n+1] == 0)
            break;
        res+=a[n+1];
        for(i = n+1;i!=0;i=p[i]){
            e[id[i]].c -= a[n+1];
            e[id[i]^1].c += a[n+1];
        }
    }
    return res;
}
int main(){
    while(scanf("%d %d %d",&m,&c,&k)!=EOF){
        int i,j,w,low,high,mid;
        n = m+c;
        high = 0;
        for(i = 1;i<=n;i++)
            for(j = 1;j<=n;j++){
                scanf("%d",&dis[i][j]);
                if(i!=j && dis[i][j] == 0)
                    dis[i][j] = INF;
            }
        for(w = 1;w<=n;w++)
            for(i = 1;i<=n;i++)
                for(j = 1;j<=n;j++)
                    dis[i][j] = min(dis[i][j],dis[i][w]+dis[w][j]);
        high = 46000;
        low = 0;
        while(low < high){
            mid = (low+high)>>1;
            create(mid);
            if(maxflow() == c)
                high = mid;
            else
                low = mid+1;
        }
        printf("%d\n",low);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值