题目链接: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;
}