POJ2112_Optimal Milking_最大流解决匹配问题

来源:互联网 发布:9.3越狱软件源 编辑:程序博客网 时间:2024/06/08 17:07

题意

有 K 台机器和 C 头牛,可以抽象成 K + C 个点。给出所有点之间的直接路径的距离,用邻接矩阵表示,不存在的路径和点与自身的路径用 0 表示。每一头牛要匹配一台机器,每一台机器至多匹配 M 头牛,题目保证每一头牛都能匹配上一台机器。求最佳匹配方案,使跑路最远的牛跑的路最短。

思路

首先看到问题使最小化最大值,于是便想到了二分枚举法。于是,怎么检验答案呢。
看到机器与牛有对应关系,并且机器有一个容量,从而想到了网络流的做法。具体算法过程如下:

1.预处理

读入邻接矩阵后,用 floyd 算法求出每一点到其他所有点的最短路。注意 i == j 时的处理,并且因为 0 的存在,这里可以剪枝。(AC代码中的 floyd 函数就是加入了剪枝的)。

2.二分答案

以 0 为下界,inf 为上界进行二分枚举(可以从 floyd 中返回最长路径加以优化)。然后检验最大流是否等于牛的数量。以下假设当前枚举的答案为 x。

3.建图

设一个源点和一个汇点。
从原点出发向每一台机器连一条边,容量为 M。
从每一头牛出发向汇点连一条边,容量为1(每头牛只能匹配一台机器,为此wa了好几次)。
从每一台机器出发,向它能到达的牛连一条边,容量为1。能到达的意思是,距离小于 x 的,距离大于 x 的直接忽略,不用连边。

注意

因为 x 的值对图的影响,每一次二分枚举都要重新建图。

4.Dinic 算法求最大流

PS

这个题还可以用二分图的多重匹配做,即把每一个点分裂成 M 个点,然后进行二分图匹配。

题目链接

http://poj.org/problem?id=2112

AC代码

#include<cstdio>#include<iostream>#include<vector>#include<queue>#include<cstring>using namespace std;struct edge{    int to, cap, rev;    edge(int a, int b, int c)    :to(a), cap(b), rev(c)    {}};const int maxn = 250;const int inf  = 70000;int K, C, M;int n;vector<edge>G[maxn];int Map[maxn][maxn];int level[maxn];int iter [maxn];void floyd(){    for(int k= 1; k<= n; k++)        for(int i= 1; i<= n; i++)        if(Map[i][k] != inf)        for(int j= 1; j<= n; j++)            if(Map[k][j] != inf) Map[i][j] = min(Map[i][j], Map[i][k] + Map[k][j]);}void Add(int from, int to, int cap){    G[from].push_back(edge(to, cap, G[to].size()));    G[to].push_back(edge(from, 0, G[from].size() - 1));}void bfs(int s){    memset(level, -1, sizeof level);    level[s] = 0;    queue<int> qu;    qu.push(s);    while(qu.size())    {        int v = qu.front();qu.pop();        for(int i= 0; i< G[v].size(); i++)        {            edge e = G[v][i];            if(e.cap > 0 && level[e.to] < 0)            {                level[e.to] = level[v] + 1;                qu.push(e.to);            }        }    }}int dfs(int v, int t, int f){    if(v == t) return f;    for(int &i= iter[v]; i< G[v].size(); i++)    {        edge &e = G[v][i];        if(e.cap > 0 && level[e.to] > level[v])        {            int d = dfs(e.to, t, min(f, e.cap));            if(d > 0)            {                e.cap -= d;                G[e.to][e.rev].cap += d;                return d;            }        }    }    return 0;}int max_flow(int s, int t){    int flow = 0;    while(1)    {        bfs(s);        if(level[t] < 0) return flow;        memset(iter, 0, sizeof iter);        while(1)        {            int f = dfs(s, t, inf);            if(f == 0) break;            flow += f;        }    }}void init(int s, int t, int x){    for(int i= s; i<= t; i++)        G[i].clear();    for(int i= 1; i<= K; i++)        Add(s, i, M);    for(int i= K+1; i<= n; i++)        Add(i, t, 1);    for(int i= 1; i<= K; i++)        for(int j= K+1; j<= n; j++)    {        if(Map[i][j] > x) continue;        Add(i, j, 1);    }}bool Judge(int x){    int s = 0, t = n + 1;    init(s, t, x);    return max_flow(s, t) >= C;}int main(){    scanf("%d %d %d", &K, &C, &M);    n = K + C;    for(int i= 1; i<= n; i++)        for(int j= 1; j<= n; j++)        {            scanf("%d", &Map[i][j]);            if(Map[i][j] == 0) Map[i][j] = inf;        }    floyd();    int lb = 0, ub = inf;    while(lb <= ub)    {        int mid = (lb + ub) >> 1;        if(Judge(mid)) ub = mid - 1;        else lb = mid + 1;    }    cout << ub + 1 << endl;    return 0;}
原创粉丝点击