解题报告 之 POJ2112 Optimal Milking

解题报告 之 POJ2112 Optimal Milking


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


题目大意:有K个挤奶器,C个奶牛分布在一个操场上,每个奶牛和机器都看做一个节点,他们两两之间或许有一条长度为dis的路,也可能没有。每个挤奶器一天最多挤M头奶牛,问要让所有奶牛都被挤到奶,所有奶牛所走的最长路程的最小值是多少?


分析:比较有意思的一道题。首先是求最大值的最小值,则说明二分。然后又是一道最大流的题。下面看看思路呗。注意到到题目输入0表示没有路径,即长度为 INF。首先将图输入进来,然后求全源最短路(Floyd),然后搞完之后奶牛到机器的最短路就出来了。
然后就要开始二分了,假设此时所有奶牛中需要走的最长路是mid,看看能否在mid的限制下让所有奶牛被挤奶,如果不满足则向上二分,如果满足就向下二分。那么每一次二分的判断操作是什么呢?就是求出在最长路长度为mid时,是否能让所有奶牛被挤奶。那么while(low<high){ }中就转化为一个最大流问题。

首先,将超级源点与所有奶牛相连,负载为1(一个奶牛只能被挤奶1次),然后将所有小于等于mid 的路径相连(即某奶牛与某机器),负载为1(一个奶牛只能去一个机器挤奶,其实这里也可以是INF,因为前面已有限制了,但我个人觉得这样其实更好理解一些),然后将各个机器与超级汇点相连,负载为M(一台机器最多挤M次。)然后判断一下最大流是否<C(奶牛数)。如果<则说明mid太小,否则说明可能mid太大。继续二分即可。

上代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cstdio>
using namespace std;

const int MAXN = 300;
const int MAXM = 90000;
const int INF = 0x3f3f3f3f;
struct Edge
{
	int to, next, cap;
};

Edge edge[MAXM];
int head[MAXN], level[MAXN];
int dis[250][250];
int src, des,cnt;

void floyd(int K, int C)
{
	int n = K + C;
	for (int k = 1; k <= n; k++)
	for (int i = 1; i <= n; i++)
	for (int j = 1; j <= n; j++)
	if (dis[i][j] > dis[i][k] + dis[k][j])
		dis[i][j] = dis[i][k] + dis[k][j];

}

void addedge(int from,int to,int cap)
{
	edge[cnt].to = to;
	edge[cnt].cap = cap;
	edge[cnt].next = head[from];
	head[from] = cnt++;

	edge[cnt].to = from;
	edge[cnt].cap = 0;
	edge[cnt].next = head[to];
	head[to] = cnt++;
}

int bfs()
{
	queue<int> q;
	while (!q.empty())
		q.pop();
	memset(level, -1, sizeof level);
	level[src] = 0;
	q.push(src);

	while (!q.empty())
	{
		int u = q.front();
		q.pop();

		for (int i = head[u]; i != -1; i = edge[i].next)
		{
			int v = edge[i].to;
			if (edge[i].cap&&level[v] == -1)
			{
				level[v] = level[u] + 1;
				q.push(v);
			}
		}
	}
	return level[des] != -1;
}

int dfs(int u,int f)
{
	if (u == des) return f;
	int tem;
	for (int i = head[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].to;
		if (edge[i].cap > 0 && level[v] == level[u] + 1)
		{
			tem = dfs(v, min(f, edge[i].cap));
			if (tem > 0)
			{
				edge[i].cap -= tem;
				edge[i ^ 1].cap += tem;
				return tem;
			}
		}
	}
	level[u] = -1;
	return 0;
}

int Dinic()
{
	int ans = 0, tem;
	
	while (bfs())
	{
		while (tem=dfs(src, INF))
		{
			ans += tem;
		}
	}
	return ans;
}

int main()
{

	int K, C, M;
	int low, high, mid;
	while (scanf("%d%d%d",&K,&C,&M)==3)
	{
		

		src = 0;
		des = K + C + 1;
		for (int i = 1; i <= K + C; i++)
		{
			for (int j = 1; j <= K + C; j++)
			{
				scanf("%d", &dis[i][j]);
				if (!dis[i][j]) dis[i][j] = INF;
			}
		}

		floyd(K, C);

		for (int i = 1; i <= K + C;i++)
		for (int j = 1; j <= K + C; j++)
		{
			low = min(low, dis[i][j]);
			high = max(high, dis[i][j]);
		}

		while (low < high)
		{
			mid = ( low + high) / 2;
			memset(head, -1, sizeof(head));
			cnt = 0;
			for (int i = K + 1; i <= K + C; i++)
			{
				addedge(src, i, 1);
				for (int j = 1; j <= K; j++)
				if (dis[i][j] <= mid)
					addedge(i, j, 1);
			}
			for (int i = 1; i <= K; i++)
				addedge(i, des, M);
			if (Dinic() < C) low = mid + 1;
			else high = mid;
		}
		printf("%d\n", low);
	}
	return 0;
}

听说艳阳天,解题报告和睡大觉更配哦。



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值