题意:给定K台挤奶机器和C头牛,每台挤奶机只能供M头牛挤奶。现在给出(K + C)*(K + C)的距离矩阵,表示第i个物体到第j个物体之间的路径长度,若此值为零,则说明其间不可直接到达。问怎么安排这C头牛到K台机器挤奶,使得需要走最长路程到挤奶机器的奶牛所走的路程最少,并求出这个最小值。
思路:首先利用Floyd算法求出每个奶牛到每个挤奶机的最短距离。
此后二分答案,先假定一个最大距离的的最小值 maxdist, 对每个maxdist值,都重新构图。 构图策略是:每个奶牛和挤奶器都是一个节点,添加一个源,连边到所有奶牛节点,这些边容量都是1。添加一个汇点,每个挤奶器都连边到它。这些边的容量都是M。如果奶牛节点i和挤奶器节点j之间的距离<= maxdist,则从i节点连一条边到j节点,表示奶牛i可以到挤奶器j去挤奶。该边容量为1。该图上的最大流如果是C(奶牛数),那么就说明假设的 maxdist成立,则减小 maxdist再试。
注意二分过程,设下界为low,上界为high。当最大流等于奶牛数量时,如果maxdist值等于low则返回答案,否则是high=maxdist(传统的二分查找是high=mid-1,这里相当于等于mid,不减一);当最大流小于奶牛数量时,使low=maxdist+1。
版本2用邻接表建图,且最大流算法的队列用stl实现。
内容部分参考北大郭炜老师的课件.
#include <stdio.h>
#include <string.h>
#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b
#define N 235
int n,m,thresh;
int dis[N][N],c[N][N],a[N],p[N],q[200000];
void createmap(int res){
int i,j;
memset(c,0,sizeof(c));
for(i = 1;i<=m;i++)//源点到牛
c[0][i] = 1;
for(i = m+1;i<=n+m;i++)//机器到汇点
c[i][n+m+1] = thresh;
for(i = 0;i<n;i++)//牛到机器
for(j = n;j<n+m;j++)
if(dis[i][j] <= res)
c[j-n+1][i+m+1] = 1;
}
int maxflow(int s,int t){//最大流
int front,rear,i,res=0;
while(1){
front = rear = -1;
memset(a,0,sizeof(a));
memset(p,0,sizeof(p));
a[s] = 0x3fffffff;
q[++rear] = s;
while(front < rear){
int now = q[++front];
for(i = 0;i<=t;i++)
if(!a[i] && c[now][i]>0){
a[i] = min(a[now],c[now][i]);
p[i] = now;
q[++rear] = i;
}
}
if(!a[t])
break;
res += a[t];
for(i = t;i!=s;i=p[i]){
c[p[i]][i] -= a[t];
c[i][p[i]] += a[t];
}
}
return res;
}
int main(){
freopen("a.txt","r",stdin);
while(scanf("%d %d %d",&n,&m,&thresh)!=EOF){
int i,j,k,res,high=0,low=0;
for(i = 0;i<n+m;i++)
for(j = 0;j<n+m;j++){
scanf("%d",&dis[i][j]);
if(dis[i][j] == 0)
dis[i][j] = 0x3fffffff;
}
//floyd
for(k = 0;k<n+m;k++)
for(i = 0;i<n+m;i++)
for(j = 0;j<n+m;j++)
dis[i][j] = min(dis[i][j],dis[i][k]+dis[k][j]);
//确定二分上界high
for(i = 0;i<n;i++)
for(j = n;j<n+m;j++)
high = max(high,dis[i][j]);
//二分答案
while(low <= high){
res = (low+high)/2;
createmap(res);//每次都得重新建图
if(m == maxflow(0,n+m+1)){
if(res == low)
break;
high = res;//这是与传统二分查找不一样的地方
}else
low = res+1;
}
printf("%d\n",res);
}
return 0;
}
版本2:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
#include <cstdlib>
using namespace std;
#define INF 0x3fffffff
#define clc(s,t) memset(s,t,sizeof(s))
#define N 235
int m,c,n,k;
int dis[N][N];
struct edge{
int y,next,c;
}e[N*N*2];
int first[N],top;
void addedge(int x,int y,int c){
e[top].y = y;
e[top].c = c;
e[top].next = first[x];
first[x] = top++;
}
void add(int x,int y,int c){
addedge(x,y,c);
addedge(y,x,0);
}
void create(int d){
int i,j;
clc(first,-1);
top = 0;
for(i = 1;i<=m;i++)
add(0,i,k);
for(i = m+1;i<=m+c;i++)
add(i,n+1,1);
for(i = 1;i<=m;i++)
for(j = m+1;j<=m+c;j++)
if(dis[i][j] <= d)
add(i,j,1);
}
int maxflow(){
queue<int> q;
int i,now,res=0,p[N],a[N],id[N];
while(1){
clc(p,0);
clc(a,0);
a[0] = INF;
q.push(0);
while(!q.empty()){
now = q.front();
q.pop();
for(i = first[now];i!=-1;i=e[i].next){
if(!a[e[i].y] && e[i].c>0){
a[e[i].y] = min(a[now],e[i].c);
p[e[i].y] = now;
id[e[i].y] = i;
q.push(e[i].y);
}
}
}
if(a[n+1] == 0)
break;
res+=a[n+1];
for(i = n+1;i!=0;i=p[i]){
e[id[i]].c -= a[n+1];
e[id[i]^1].c += a[n+1];
}
}
return res;
}
int main(){
while(scanf("%d %d %d",&m,&c,&k)!=EOF){
int i,j,w,low,high,mid;
n = m+c;
high = 0;
for(i = 1;i<=n;i++)
for(j = 1;j<=n;j++){
scanf("%d",&dis[i][j]);
if(i!=j && dis[i][j] == 0)
dis[i][j] = INF;
}
for(w = 1;w<=n;w++)
for(i = 1;i<=n;i++)
for(j = 1;j<=n;j++)
dis[i][j] = min(dis[i][j],dis[i][w]+dis[w][j]);
high = 46000;
low = 0;
while(low < high){
mid = (low+high)>>1;
create(mid);
if(maxflow() == c)
high = mid;
else
low = mid+1;
}
printf("%d\n",low);
}
return 0;
}