题意:给定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; }