题目链接:http://poj.org/problem?id=2112
题意:有n个挤奶机,m头奶牛,每个挤奶机最多处理k头奶牛。给出挤奶机、奶牛之间的距离,问完成挤奶任务后,使走最远距离奶牛最小化,并输出。
思路:最远距离最小化,很容易知道时二分处理。但是二分判定条件是什么呢?
二分距离成立的情况是 使所以奶牛都可以到挤奶机。这样变成了一个二分图匹配的问题,左边是n个挤奶机, 右边是m头奶牛,每个奶牛都要匹配一个挤奶机。又因为挤奶机有匹配个数的,就是二分图多重匹配了。只要最大匹配数量是奶牛的头数m则这个二分距离是成立的。(二分图多重匹配)
当然,用网络流最大流也是可以完成的,二分图一般都可以用网络流。那怎么建图呢?
只要建两个虚点,一个起点,一个终点,起点连接每个挤奶机,流量是k,每头奶牛连接终点,流量为1,而根据二分距离判定挤奶机是否可以连接奶牛,可以则流量是1。只要最大流是m,则说明二分距离是可以的。
当然这里的点与点之间的距离用floyed计算即可.
二分图多重匹配代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn=350; int mp[maxn][maxn]; bool used[maxn]; int cnt[maxn];//cnt[i]记录现在第i个挤奶机有多少个奶牛 int mat[maxn][maxn],n,m,k;//mat[i][j]表示第i个挤奶机第j个匹配的奶牛是谁 int g[maxn][maxn]; bool dfs(int x)//匹配 { for(int i=1;i<=n;i++) { if(mp[x][i]&&!used[i])//表示第x个奶牛可以匹配第i个挤奶机 { used[i]=1; if(cnt[i]<k)//并且该挤奶机数量未上限,直接匹配 { mat[i][cnt[i]++]=x; return true; } else//人数上限,查找该挤奶机上所以奶牛是否可以让位置 { for(int j=0;j<k;j++) { if(dfs(mat[i][j]))//可以让位置 { mat[i][j]=x; return true; } } } } } return false; } bool solve() { for(int i=n+1;i<=n+m;i++)//查看是否所以奶牛可以匹配 { for(int j=1;j<=n;j++) used[j]=0; if(!dfs(i))//i奶牛不能匹配 return false; } return true; } bool check(int x)//判断条件,距离为x是否成立 { memset(cnt,0,sizeof(cnt)); memset(mp,0,sizeof(mp)); for(int i=1;i<=n;i++) for(int j=n+1;j<=m+n;j++) if(g[i][j]<=x) mp[i][j]=mp[j][i]=1; if(solve()) return true; return false; } int main() { scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=n+m;i++) { for(int j=1;j<=n+m;j++) { scanf("%d",&g[i][j]); if(i!=j&&!g[i][j]) g[i][j]=inf; } } for(int k=1;k<=n+m;k++)//floyed求最短距离 for(int i=1;i<=n+m;i++) for(int j=1;j<=n+m;j++) g[i][j]=min(g[i][j],g[i][k]+g[k][j]); int l=0,r=inf,mid; while(r>=l)//二分结果 { mid=(r+l)/2; if(check(mid)) r=mid-1; else l=mid+1; } printf("%d ",l); return 0; }
网络流代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn=350; int g[maxn][maxn]; struct node{ int u,v,w,nxt; }e[maxn*maxn]; int h[maxn],depth[maxn],n,m,k,st,ed; int cnt; void add(int u,int v,int w)//建图,记得建反向边 { e[cnt].v=v;e[cnt].w=w; e[cnt].nxt=h[u];h[u]=cnt++; e[cnt].v=u,e[cnt].w=0; e[cnt].nxt=h[v];h[v]=cnt++; } bool bfs(){//dinic--分层图 queue<int> que; memset(depth,0,sizeof(depth)); que.push(st); depth[st]=1; while(!que.empty()){ int u=que.front(); que.pop(); if(u==ed) return true; for(int i=h[u];i!=-1;i=e[i].nxt){ int v=e[i].v; int w=e[i].w; if(!depth[v]&&w){ depth[v]=depth[u]+1; que.push(v); } } } return false; } int dfs(int u,int dis) { if(u==ed) return dis; int res=0; for(int i=h[u];i!=-1;i=e[i].nxt) { int v=e[i].v; int w=e[i].w; if((depth[v]==depth[u]+1)&&w) { int di=dfs(v,min(w,dis-res)); e[i].w-=di; e[i^1].w+=di; res+=di; if(res==dis) return dis; } } return res; } int dinic()//dinic求最大流 { int ans=0; while(bfs()) { ans+=dfs(st,inf); } return ans; } bool check(int x) { cnt=0; memset(h,-1,sizeof(h)); for(int i=1;i<=n;i++)//挤奶机和奶牛是否可以连接 for(int j=n+1;j<=n+m;j++) if(g[i][j]<=x) add(i,j,1); for(int i=1;i<=n;i++)//起点连挤奶机 add(st,i,k); for(int i=n+1;i<=m+n;i++)//奶牛连终点 add(i,ed,1); int ans=dinic(); if(ans==m)//最大流是奶牛头数 return true; return false; } int main() { scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=n+m;i++) { for(int j=1;j<=n+m;j++) { scanf("%d",&g[i][j]); if(i!=j&&!g[i][j]) g[i][j]=inf; } } for(int k=1;k<=n+m;k++) for(int i=1;i<=n+m;i++) for(int j=1;j<=n+m;j++) g[i][j]=min(g[i][j],g[i][k]+g[k][j]); st=0,ed=n+m+1;//建起点,终点 int l=0,r=inf,mid; while(r>=l) { mid=(r+l)/2; if(check(mid)) r=mid-1; else l=mid+1; } printf("%d ",l); return 0; }