【题目】http://poj.org/problem?id=2112
【题意】农场里有c个牛和k个机器,牛和机器之间有有向边,每个机器能承受m个牛的访问,已知这些边的边长,在保证每个牛都能访问机器的情况下,求所有牛中一头牛的最长路的最小值。
【参考】https://blog.csdn.net/maxichu/article/details/45060573
【思路】首先二分,假设取的路中,最大值是mid,然后无视掉所有大于mid的路,然后每当牛到机器的路<mid,就加一条牛到机器容量为1的边,源点对牛建边,容量是1,机器到汇点建边,容量就是机器的限制。
【重点】二分,建边,EK
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
const int M=2505;
const int inf=99999999;
int mp[M][M];
int mp2[M][M];
int k,c,m;
int start,end;
int pathf[M];
int temp,ans;
std::queue<int>q;
void bfs(int x)
{
while(!q.empty())
q.pop();
pathf[x]=0;
q.push(x);
while(!q.empty())
{
int t=q.front();
if(t==end)
return;
for(int i=0; i<=end; i++)//
{
if(mp[t][i]>0&&pathf[i]==-1)
{
pathf[i]=t;//现在path不是一个个存了,path[a]里存的是a的前一个节点。
q.push(i);
}
}
q.pop();
}
return;
}
int main()
{
while(~scanf("%d%d%d",&k,&c,&m))
{
for(int i=0; i<=k+c+1; i++)
{
for(int j=0; j<=k+c+1; j++)
{
mp2[i][j]=inf;
}
}
for(int i=1; i<=k+c; i++)
{
for(int j=1; j<=k+c; j++)
{
int t;
scanf("%d",&t);
if(t)
mp2[i][j]=t;
}
}
for(int p=1; p<=k+c; p++)
{
for(int i=1; i<=k+c; i++)
{
for(int j=1; j<=k+c; j++)
{
mp2[i][j]=std::min(mp2[i][j],mp2[i][p]+mp2[p][j]);
}
}
}
int MIN=inf,MAX=-1;
for(int i=1; i<=k+c; i++)
{
for(int j=1; j<=k+c; j++)
{
MIN=std::min(mp2[i][j],MIN);
MAX=std::max(mp2[i][j],MAX);
}
}
while(MIN<MAX)
{
ans=0;
memset(mp,0,sizeof(mp));
int MID=(MIN+MAX)/2;
start=0,end=k+c+1;
for(int i=k+1; i<=k+c; i++)//如果边长小于mid就取这条牛到机器的边
{
mp[start][i]=1;//源点到牛的距离为1
for(int j=1; j<=k; j++)
{
if(mp2[i][j]>MID)
continue;
mp[i][j]=1;
}
}
for(int i=1; i<=k; i++)//机器到汇点的边长是机器的限制
mp[i][end]=m;
while(1)
{
temp=inf;
memset(pathf,-1,sizeof(pathf));
bfs(start);
int now=end;
if(pathf[end]==-1)
break;
while(now!=start)
{
int pre=pathf[now];//
temp=std::min(temp,mp[pre][now]);
now=pre;
}
if(temp==inf)
break;
now=end;
while(now!=start)
{
int pre=pathf[now];
mp[pre][now]-=temp;
mp[now][pre]+=temp;
now=pre;
}
ans+=temp;
}
if(ans<c)//如果最长路在mid的限制下,最后并不是所有的牛都走到了汇点,那么放宽限制,让可取的最长路多一些。
MIN=MID+1;
else//如果当前就满足了,那么就看看小一些能不能行。
MAX=MID;
}
printf("%d\n",MIN);
}
}