<<训练指南>>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()); } }