题意
有 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;
}