题意: 给你 n 个机器,m 头牛,每台机器最多可供 cap 头牛使用,并告诉你每个物体之间的距离,找出一个距离满足所有的牛都分配到机器且里面走最远的牛
走的距离最小。
分析: 用 floyd 计算出每个物体的最短距离,然后二分枚举距离,如果在该距离下满足完全匹配,该距离即为合法距离,匹配用到多重匹配。
二分图多重匹配
#include<stdio.h> #include<string.h> #define clr(x)memset(x,0,sizeof(x)) int cap; int link[33][202]; int vlink[33]; int v[33]; struct node { int to,next; }q[200000]; int head[202]; int g[240][240]; int tot; int n,m; void add(int s,int u) { q[tot].to=u; q[tot].next=head[s]; head[s]=tot++; } int find(int x) { int i,j,k; for(i=head[x];i;i=q[i].next) { k=q[i].to; if(!v[k]) { v[k]=1; if(vlink[k]<cap) { link[k][vlink[k]++]=x; return 1; } for(j=0;j<vlink[k];j++) { if(find(link[k][j])) { link[k][j]=x; return 1; } } } } return 0; } int ok(int ti) { int i,j,sum=0; tot=1; clr(head); clr(vlink); for(i=n+1;i<=n+m;i++) for(j=1;j<=n;j++) if(g[i][j]&&g[i][j]<=ti) add(i-n,j); for(i=1;i<=m;i++) { clr(v); if(find(i)) sum++; else break; } if(sum==m) return 1; return 0; } int main() { int i,j,k,low,high,mid; while(scanf("%d%d%d",&n,&m,&cap)!=EOF) { for(i=1;i<=n+m;i++) for(j=1;j<=n+m;j++) scanf("%d",&g[i][j]); for(k=1;k<=n+m;k++) for(i=1;i<=n+m;i++) { if(g[i][k]) for(j=1;j<=n+m;j++) if(g[k][j]&&(g[i][k]+g[k][j]<g[i][j]||g[i][j]==0)) g[i][j]=g[i][k]+g[k][j]; } low=0; high=0; for(i=n+1;i<=n+m;i++) for(j=1;j<=n;j++) if(g[i][j]>high) high=g[i][j]; while(low<high) { mid=(low+high)>>1; if(ok(mid)) high=mid; else low=mid+1; } printf("%d\n",low); } return 0; }
最大流做法:
#include<stdio.h> #include<string.h> #define INF 0x1f; int n,m,s,u,e; int cap; int c[232][232]; int gap[232]; int dis[232]; int g[232][232]; void init() { int v,x,q[232],front=0,rear=0; memset(gap,0,sizeof(gap)); memset(dis,0xff,sizeof(dis)); q[rear++]=u; dis[u]=0; while(front<rear) { x=q[front++]; gap[dis[x]]++; for(v=0;v<=e;v++) if(dis[v]==-1&&c[v][x]>0) { dis[v]=dis[x]+1; q[rear++]=v; } } } int sap() { init(); int flow=0,top=s,i,j,k,pre[232],low[232]; //memset(low,0,sizeof(low)); while(dis[s]<=e) { int flag=0; low[s]=INF; for(i=0;i<=e;i++) if(c[top][i]>0&&dis[top]==dis[i]+1&&dis[i]>=0) { flag=1; break; } if(flag) { low[i]=c[top][i]; if(low[i]>low[top]) low[i]=low[top]; pre[i]=top; top=i; if(top==u) { flow+=low[u]; j=top; while(j!=s) { k=pre[j]; c[k][j]-=low[u]; c[j][k]+=low[u]; j=k; } top=s; memset(low,0,sizeof(low)); } } else { int min=e; for(j=0;j<=e;j++) if(c[top][j]>0&&dis[j]+1<min&&dis[j]>=0) min=dis[j]+1; gap[dis[top]]--; if(gap[dis[top]]==0) break; gap[min]++; dis[top]=min; if(top!=s) top=pre[top]; } } return flow; } int ok(int ti) { int i,j,sum; memset(c,0,sizeof(c)); s=0; // 超级源点 s 0 e=u=n+m+1; // 超级汇点 u n+m+1 for(i=n+1;i<=n+m;i++) // 牛的编号为 n+1..n+m c[0][i]=1; // 机器的编号为 1..n for(i=n+1;i<=n+m;i++) for(j=1;j<=n;j++) if(g[i][j]&&g[i][j]<=ti) // 如果牛和机器之间有边,且边距离小于 ti, c[i][j]=1; // 容量设为 1 for(i=1;i<=n;i++) c[i][u]=cap; // 机器和汇点之间连边 sum=sap(); // 求最大流 if(sum==m) // 最大流为 牛的数量 return 1; return 0; } int main() { int i,j,k,low,high,mid,t; while(scanf("%d%d%d",&n,&m,&cap)!=EOF) { for(i=1;i<=n+m;i++) for(j=1;j<=n+m;j++) scanf("%d",&g[i][j]); for(k=1;k<=n+m;k++) for(i=1;i<=n+m;i++) { if(g[i][k]) for(j=1;j<=n+m;j++) if(g[k][j]&&(g[i][k]+g[k][j]<g[i][j]||g[i][j]==0)) g[i][j]=g[i][k]+g[k][j]; } low=0; high=0; for(i=n+1;i<=n+m;i++) for(j=1;j<=n;j++) if(g[i][j]>high) high=g[i][j]; while(low<high) // 二分枚举答案 { mid=(low+high)>>1; if(ok(mid)) // 枚举的答案可以保证 每头牛都分配到机器 high=mid; else low=mid+1; } printf("%d\n",low); } return 0; }