poj2112 Optimal Milking

poj2112 Optimal Milking 解题报告

题目原文

Time Limit: 2000MS   Memory Limit: 30000K
Total Submissions: 15074   Accepted: 5386
Case Time Limit: 1000MS

Description

FJ has moved his K (1 <= K <= 30) milking machines out into the cow pastures among the C (1 <= C <= 200) cows. A set of paths of various lengths runs among the cows and the milking machines. The milking machine locations are named by ID numbers 1..K; the cow locations are named by ID numbers K+1..K+C. 

Each milking point can "process" at most M (1 <= M <= 15) cows each day. 

Write a program to find an assignment for each cow to some milking machine so that the distance the furthest-walking cow travels is minimized (and, of course, the milking machines are not overutilized). At least one legal assignment is possible for all input data sets. Cows can traverse several paths on the way to their milking machine. 

Input

* Line 1: A single line with three space-separated integers: K, C, and M. 

* Lines 2.. ...: Each of these K+C lines of K+C space-separated integers describes the distances between pairs of various entities. The input forms a symmetric matrix. Line 2 tells the distances from milking machine 1 to each of the other entities; line 3 tells the distances from machine 2 to each of the other entities, and so on. Distances of entities directly connected by a path are positive integers no larger than 200. Entities not directly connected by a path have a distance of 0. The distance from an entity to itself (i.e., all numbers on the diagonal) is also given as 0. To keep the input lines of reasonable length, when K+C > 15, a row is broken into successive lines of 15 numbers and a potentially shorter line to finish up a row. Each new row begins on its own line. 

Output

A single line with a single integer that is the minimum possible total distance for the furthest walking cow. 

Sample Input

2 3 2
0 3 2 1 1
3 0 3 2 0
2 3 0 1 0
1 2 1 0 2
1 0 0 2 0

Sample Output

2

Source

题目大意

题目大意是说有K个挤奶机和C头奶牛,1~K节点是挤奶机,K+1~K+N节点是奶牛。每个挤奶机每天最多能让M头奶牛挤奶,问:在让所有的奶牛都能够找到挤奶机挤奶的情况下,怎样安排每头奶牛的行走路线,能够使得走的路最多的奶牛走的路最少,求出这个最小值。

解析

设一个S点和一个T点,由S点向每一个奶牛点连容量为1的边,由每个挤奶机点向T点连容量为M的边,再由每个奶牛点向它能到达的挤奶机连容量为1的边,边权为这头奶牛到这个机器的最短路(floyd预处理)。

然后二分枚举允许奶牛走的最长的路程,假设这个最长的路程为mid。并把网络流途中边权大于mid的边都删去,跑一遍最大流,如果最大流等于C,就说明所有奶牛走的路程都小于等于mid的情况下,所有奶牛都能到达挤奶机。反之,如果最大流小于C,说明所有的奶牛走的路程都小于等于mid的情况下,有些奶牛无法分配到挤奶机挤奶。

时间复杂度

假如所有边的权值都是200(题中说的上限),图又是一条链,则距离最远的两个点的距离是46000,二分的复杂度就是log46000,大约是12。每次建图的复杂度是O(CK)的,而做一次Dinic的复杂度是O((C+K)^2*CK),总的就是O( 12 * (C+K)^2 * CK ),取最大值计算,即是12*(200+30)*(200*30)=1.656*10^7,但实际上我每次mid减小就会少好多条边,最大边权也不会总是那么极端,因此实际的计算次数一定会远远小于这个值,此算法是可行的。

15152009 710395641 2112 Accepted 3132K 188MS G++ 2162B 2016-02-10 19:38:38

代码

//poj2112  Optimal Milking 二分+最大流
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define maxn 500
#define inf 0x3f3f3f3f
#define S N+K+1
#define T N+K+2
using namespace std;
int N, K, M, cost[maxn][maxn], head[maxn], next[maxn*maxn], c[maxn*maxn],
	to[maxn*maxn], tot=1, mid, f[maxn*maxn], tot_flow, deep[maxn];
queue<int> q;
void add(int a, int b, int v)
{
	to[++tot]=b;c[tot]=v;
	next[tot]=head[a];head[a]=tot;
	if(~tot&1)add(b,a,0);
}
void input()
{
	int i, j;
	for(i=1;i<=N+K;i++)
		for(j=1;j<=N+K;j++)
		{
			scanf("%d",&cost[i][j]);
			if(cost[i][j]==0&&i!=j)cost[i][j]=inf;
		}
}
void floyd()
{
	int i, j, k;
	for(k=1;k<=N+K;k++)
		for(i=1;i<=N+K;i++)
			for(j=1;j<=N+K;j++)
				if(cost[i][j]>cost[i][k]+cost[k][j])
					cost[i][j]=cost[i][k]+cost[j][k];
}
void setmap()
{
	int i, j;
	memset(head,0,sizeof(head));tot=1;
	memset(next,0,sizeof(next));
	for(i=1;i<=N;i++)add(S,K+i,1);
	for(i=1;i<=K;i++)add(i,T,M);
	for(i=1;i<=N;i++)for(j=1;j<=K;j++)
		if(cost[K+i][j]<=mid)add(K+i,j,cost[K+i][j]);
}
bool BFS()
{
	int p, x;
	memset(deep,-1,sizeof(deep)); 
	q.push(S);deep[S]=0;
	while(!q.empty())
	{
		x=q.front();q.pop();
		for(p=head[x];p;p=next[p])
			if(deep[ to[p] ]==-1 && c[p]-f[p])
				deep[to[p]]=deep[x]+1,q.push(to[p]);
	}
	return deep[T]!=-1;
}
int dfs(const int x, const int flow)
{
	int p, t;
	if(x==T)return flow;
	for(p=head[x];p;p=next[p])
		if( deep[to[p]]==deep[x]+1 && c[p]-f[p] 
			&& (t=dfs(to[p],min(flow,c[p]-f[p]))) )
			{f[p]+=t,f[p^1]-=t;return t;}
	return 0;
}
bool check()
{
	int i, j, t;
	setmap();
	tot_flow=0;
	memset(f,0,sizeof(f));
	while(BFS())
		while(t=dfs(S,0x7fffffff))tot_flow+=t;
	return tot_flow==N;
}
int main()
{
	int i, j, maxx, l, r;
	while(scanf("%d%d%d",&K,&N,&M)!=EOF)
	{
		input();
		floyd();
		maxx=-inf;
		for(i=1;i<=N+K;i++) for(j=1;j<=N+K;j++)
			maxx=max(maxx,cost[i][j]);
		l=1,r=maxx,mid=(l+r)>>1;
		while(l<r)
		{
			if(check())r=mid;
			else l=mid+1;
			mid=(l+r)>>1;
		}
		printf("%d\n",r);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值