<<训练指南>>P367页提到的公平分配问题。
二分答案ans
建模:
构造二分图,奶牛看成X集合,挤奶机看成Y集合。
1、X和源S连边,边容量为1.
2、满足dist(x,y)<=ans的 X和Y连边,边容量为1
3、Y和汇T连边,边容量为m.(不能超过挤奶机上限)
这样,网络中的流量都是由S——》X——》Y——》T点 ,只有当网络的总流量等于K时,才意味着每一个奶牛都选择了一个挤奶器,也就是一个可行解。
通过log(n)次的最大流既可以求出答案。
dinic实现。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 250
#define INF 0x3f3f3f3f
struct edge
{
int to,c,next;
};
edge e[999999];
int que[MAXN*100];
int dis[MAXN];
int pre[MAXN];
int head[MAXN],head2[MAXN];
int mp[MAXN][MAXN];
int st,ed;
int maxflow;
int en;
int n,m,k,c;
void add(int a,int b,int c)
{
e[en].to=b;
e[en].c=c;
e[en].next=head[a];
head[a]=en++;
e[en].to=a;
e[en].c=0;
e[en].next=head[b];
head[b]=en++;
}
bool bfs()
{
memset(dis,-1,sizeof(dis));
que[0]=st,dis[st]=1;
int t=1,f=0;
while(f<t)
{
int j=que[f++];
for(int k=head[j];k!=-1;k=e[k].next)
{
int i=e[k].to;
if(dis[i]==-1&&e[k].c)
{
que[t++]=i;
dis[i]=dis[j]+1;
if(i==ed) return true;
}
}
}
return false;
}
int update()
{
int p,flow=INF;
for (int i=pre[ed];i!=-1;i=pre[i])
if(e[head2[i]].c<flow) p=i,flow=e[head2[i]].c;
for (int i=pre[ed];i!=-1;i=pre[i])
e[head2[i]].c-=flow,e[head2[i]^1].c+=flow;
maxflow+=flow;
return p;
}
void dfs()
{
memset(pre,-1,sizeof(pre));
memcpy(head2,head,sizeof(head2));
for(int i=st,j;i!=-1;)
{
int flag=false;
for(int k=head[i];k!=-1;k=e[k].next)
if(e[k].c&&(dis[j=e[k].to]==dis[i]+1))
{
pre[j]=i;
head2[i]=k;
i=j;
flag=true;
if(i==ed)
i=update();
if(flag)
break;
}
if (!flag) dis[i]=-1,i=pre[i];
}
}
int dinic()
{
maxflow=0;
while(bfs())
dfs();
return maxflow;
}
void init()
{
en=0;
st=k+c+1; //源
ed=k+c+2; //汇
memset(head,-1,sizeof(head));
}
int l,r,mid;
void input()
{
for(int i=1;i<=k+c;i++)
{
for(int j=1;j<=k+c;j++)
{
scanf("%d",&mp[i][j]);
if(mp[i][j]==0) mp[i][j]=INF;
}
}
int n=k+c;
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(mp[i][j]>mp[i][k]+mp[k][j])
mp[i][j]=mp[i][k]+mp[k][j];
}
}
}
}
void build(int mid)
{
init();
for(int i=1;i<=k;i++) add(i,ed,m);
for(int j=k+1;j<=k+c;j++) add(st,j,1);
for(int i=k+1;i<=k+c;i++)
{
for(int j=1;j<=k;j++)
{
if(mp[i][j]<=mid)
{
add(i,j,1);
}
}
}
}
int cal()
{
l=0;r=20000;
int ans=0;
while(l<=r)
{
mid=(l+r)>>1;
build(mid);
if(dinic()==c) {ans=mid;r=mid-1;}
else l=mid+1;
}
return ans;
}
int main()
{
while(scanf("%d%d%d",&k,&c,&m)!=EOF)
{
input();
printf("%d\n",cal());
}
}