题意:安排C头奶牛到K个挤奶器,每个挤奶器最多能为M头牛挤奶,给出奶牛、挤奶器之间的额边的权值,求所有安排方案中,C头奶牛需要走的最大距离的最小值
先求好原图中两两间的最短路径,再二分答案,验证是否能满足C头奶牛都能到达挤奶器,能的话就继续缩小范围
具体验证方法:增加一个汇点,每个挤奶器向汇点连上一条容量为M的边,增加一个源点,向每只牛连一条容量为1的边,然后求一个s--t的最大流即可
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<queue>
#define arr 300
#define MAX 99999999
#define min(a,b)(a<b?a:b)
using namespace std;
int map[arr][arr],pre[arr],flow[arr];
int n,m,start,end;
int K,C,M;
int BFS()
{
int i,j,k,v,u;
memset(pre,-1,sizeof(pre));
for(i=0;i<=n;i++) flow[i]=MAX;
queue<int>que;
pre[start]=0;
que.push(start);
while(!que.empty())
{
v=que.front();
que.pop(); //清空队列;
for(i=0;i<=n;i++)
{
u=i;
if(u==start||pre[u]!=-1||map[v][u]==0) continue;
pre[u]=v;
flow[u]=min(flow[v],map[v][u]);
que.push(u);
}
}
if(flow[end]==MAX) return -1;
return flow[end];
}
int Edmonds_Karp()
{
int i,j,k,temp,now,last,ans=0;
while(1)
{
temp=BFS();
if(temp==-1) break;
ans+=temp;
now=end;
while(now!=start)
{
last=now;
now=pre[now];
map[now][last]-=temp;
map[last][now]+=temp;
}
}
return ans;
}
int dis[arr][arr];
void build(int x)
{
int i,j;
memset(map,0,sizeof(map));
for(i=1+K;i<=n-1;i++)
{
for(j=1;j<=K;j++)
{
if(dis[i][j]<=x) map[i][j]=1;
}
}
for(i=1;i<=K;i++) map[i][n]=M;
for(i=K+1;i<=K+C;i++) map[0][i]=1;
}
int main()
{
int i,j,k;
scanf("%d%d%d",&K,&C,&M);start=0,end=K+C+1;
n=K+C+1;
for(i=1;i<=K+C;i++)
{
for(j=1;j<=K+C;j++)
{
scanf("%d",&dis[i][j]);
if(dis[i][j]==0) dis[i][j]=MAX;
}
}
for(k=1;k<=K+C;k++)
{
for(i=1;i<=K+C;i++)
{
for(j=1;j<=K+C;j++)
{
if(dis[i][k]+dis[k][j]<dis[i][j])
dis[i][j]=dis[i][k]+dis[k][j];
}
}
}
int l=0,r=10000,mid;
int best=-1;
while(l<=r)
{
mid=(l+r)>>1;
build(mid);
int tmp=Edmonds_Karp();
if(tmp>=C)
{
best=mid;
r=mid-1;
}
else l=mid+1;
}
printf("%d\n",best);
return 0;
}