POJ2112 Optimal Milking(最大流+二分答案)

题意: 有K个挤奶器,和C头牛,让你求所有牛到挤奶器的最近距离的最远是多大。


题解: 由于挤奶器的不唯一,所以我们不能直接的求,然后对于多源点的求解问题,我们应该很直观的会有一个想法,那就是最大流能不能搞?

事实证明这个方法是可行的,我们用folyd预处理求出两点间的最短距离,然后二分距离,如果当前距离是我们构成的流的最大值等于牛的个数那么证明当前距离是可行的方案,然后继续二分直至结束。

至于怎么构图呢?我们可以设立一个超级源点和超级汇点,然后把所有的挤奶器和源点建立一条容量为M的边,所有挤奶器和牛建立一天容量为1的边,所有牛和汇点建立一条边即可。


一个不错的最大流和二分答案的结合。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int maxn = 350;
int head[maxn],level[maxn],cnt,Max_Flow;
struct Info{
	int from,to,c,flow,next;
}edge[maxn*maxn*2];
void ADD(int u,int v,int w){
	edge[cnt].from = u;
	edge[cnt].to = v;
	edge[cnt].c = edge[cnt].flow = w;
	edge[cnt].next = head[u];
	head[u] = cnt++;

	edge[cnt].from = v;
	edge[cnt].to = u;
	edge[cnt].c = edge[cnt].flow = 0;
	edge[cnt].next = head[v];
	head[v] = cnt++;
}
int K,C,M,dist[maxn][maxn];
void Make_map(int Max_dist){
	memset(head,-1,sizeof(head));
	cnt = Max_Flow = 0;
	for(int i = 1; i <= K; i++)
		ADD(i,K+C+1,M);
	for(int i = K+1; i <= K+C; i++)
		ADD(0,i,1);
	for(int i = K+1; i <= K+C; i++)
		for(int j = 1; j <= K; j++)
			if(dist[i][j] <= Max_dist)
				ADD(i,j,1);
}
int floyd(){
	int max_dist = 0;
	for(int k = 1; k <= K+C; k++)
		for(int i = 1; i <= K+C; i++){
			if(dist[i][k] == INF)continue;
			for(int j = 1; j <= K+C; j++){
				if(dist[k][j] < INF){
					dist[i][j] = min(dist[i][j],dist[i][k]+dist[k][j]);
					max_dist = max(max_dist,dist[i][j]);
				}
			}
		}
	return max_dist;
}
int bfs(int S,int E){
	memset(level,-1,sizeof(level));
	queue<int>Q;
	level[S] = 0;
	Q.push(S);
	while(!Q.empty()){
		int u = Q.front();
		Q.pop();
		for(int i = head[u]; ~i; i = edge[i].next){
			int v = edge[i].to;
			if(level[v] == -1 && edge[i].flow > 0){
				level[v] = level[u]+1;
				Q.push(v);
				if(v == E)
					return 1;
			}
		}
	}
	return 0;
}
int Find(int now,int flow,int E){
	if(now == E)
		return flow;
	int os = flow;
	for(int i = head[now]; ~i; i = edge[i].next){
		int v = edge[i].to;
		if(level[v] == level[now]+1 && edge[i].flow > 0){
			int temp = Find(v,min(flow,edge[i].flow),E);
			if(!temp)continue;
			edge[i].flow -= temp;
			edge[i^1].flow += temp;
			flow -= temp;
		}
	}
	return os-flow;
}
int Dinic(int S,int E){
	while(bfs(S,E)){
		int temp = Find(S,INF,E);
		Max_Flow += temp;
	}
	return Max_Flow;
}
int main(){
	while(~scanf("%d %d %d",&K,&C,&M)){
		memset(dist,0,sizeof(dist));
		for(int i = 1; i <= K+C; i++)
			for(int j = 1; j <= K+C; j++){
				scanf("%d",&dist[i][j]);
				if(dist[i][j] == 0)
					dist[i][j] = INF;
			}
		int l = 0,r = floyd();
		int S = 0,E = C+K+1,ans;
		while(l <= r){
			int Mid = (l+r)>>1;
			Make_map(Mid);
			int res = Dinic(S,E);
			if(res == C){
				ans = Mid;
				r = Mid-1;
			}
			else
				l = Mid+1;
		}
		printf("%d\n",ans);
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值