题目链接:http://poj.org/problem?id=2112
题意: K台挤奶机分布在C头牛间,每台最多同时服务M头牛。给出距离矩阵,求最小化牛的最大路程?
解法:
一看到最小化最大值(或者最大化最小值),当然是二分搜索了。
先将两点间最短路预处理出来,可以简单warshall_floyd搞定。
接着构建网络流,从s到牛引一条容量1的边,从挤奶机到t引一条容量M的边,
然后对最大路径二分,在二分图匹配建图的时候,牛到挤奶机间距离大于limit的边忽略掉。二分判断条件是,最大流的
流量是否等于牛量,因为题目明确表示至少有一个解使得所有牛都有挤奶机可用。
//POJ 1698
#include <queue>
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
const int maxn = 410;
const int maxm = 40010;
const int INF = 0x3fffffff;
struct G
{
int v, cap, next;
G() {}
G(int v, int cap, int next) : v(v), cap(cap), next(next) {}
} E[maxm];
int p[maxn], T;
int d[maxn], temp_p[maxn], qw[maxn]; //d顶点到源点的距离标号,temp_p当前狐优化,qw队列
void init()
{
memset(p, -1, sizeof(p));
T = 0;
}
void add(int u, int v, int cap)
{
E[T] = G(v, cap, p[u]);
p[u] = T++;
E[T] = G(u, 0, p[v]);
p[v] = T++;
}
bool bfs(int st, int en, int n)
{
int i, u, v, head, tail;
for(i = 0; i <= n; i++) d[i] = -1;
head = tail = 0;
d[st] = 0;
qw[tail] = st;
while(head <= tail)
{
u = qw[head++];
for(i = p[u]; i + 1; i = E[i].next)
{
v = E[i].v;
if(d[v] == -1 && E[i].cap > 0)
{
d[v] = d[u] + 1;
qw[++tail] = v;
}
}
}
return (d[en] != -1);
}
int dfs(int u, int en, int f)
{
if(u == en || f == 0) return f;
int flow = 0, temp;
for(; temp_p[u] + 1; temp_p[u] = E[temp_p[u]].next)
{
G& e = E[temp_p[u]];
if(d[u] + 1 == d[e.v])
{
temp = dfs(e.v, en, min(f, e.cap));
if(temp > 0)
{
e.cap -= temp;
E[temp_p[u] ^ 1].cap += temp;
flow += temp;
f -= temp;
if(f == 0) break;
}
}
}
return flow;
}
int dinic(int st, int en, int n)
{
int i, ans = 0;
while(bfs(st, en, n))
{
for(i = 0; i <= n; i++) temp_p[i] = p[i];
ans += dfs(st, en, INF);
}
return ans;
}
int mp[240][240];
int K, C, M, V; //
int check(int limit){
int S = V, T = V+1;
init();
for(int i = 0; i < K; i++){
add(i, T, M);
}
for(int i = K; i < V; i++){
add(S, i, 1);
}
for(int i = 0; i < K; i++){
for(int j = K; j < V; j++){
if(mp[i][j] <= limit){
add(j, i, 1);
}
}
}
int ans = dinic(S, T, T+1);
return ans == C;
}
int main()
{
scanf("%d%d%d", &K, &C, &M);
V = K+C;
for(int i = 0; i < V; i++){
for(int j = 0; j < V; j++){
int x;
scanf("%d", &x);
mp[i][j] = x ? x : INF;
}
}
//图预处理,warshall_floyd最小化两点的距离
for(int k = 0; k < V; k++){
for(int i = 0; i < V; i++){
for(int j = 0; j < V; j++){
mp[i][j] = min(mp[i][j], mp[i][k] + mp[k][j]);
}
}
}
//二分答案
int l = 0, r = 300*V, ans;
while(l <= r){
int mid = (l + r) / 2;
if(check(mid)) ans = mid, r = mid-1;
else l = mid+1;
}
printf("%d\n", ans);
return 0;
}