[网络流24题]圆桌问题
题目:洛谷P3254。
题目大意:
有n个单位和m张桌子,每个单位有一定的人数,每张桌子也有一定的容量。一张桌子上不能有两个同一单位的人。现在问你能否坐下,若能则输出一种方案。
解题思路:
最大流。
从S向每个单位连一条容量为(单位人数)的边,代表这个单位有那么多人。
从每张桌子向T连一条容量为(桌子容量)的边,代表这张桌子能做那么多人。
从每个单位向每张桌子连一条容量为1的边,代表每个单位在一张桌子上只能坐一个人。
然后若最大流量不等于总人数,则无解。否则有解。
输出方案,就枚举边,对于一条从单位流向桌子的边,若跑完剩余容量为0,则该单位在这张桌子上坐了人。
C++ Code:
#include<bits/stdc++.h> #define S 0 #define T 1000 struct edge{ int from,to,cap,nxt; }e[200000]; inline int readint(){ int c=getchar(),d=0; for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar()) d=(d<<3)+(d<<1)+(c^'0'); return d; } int cnt=1,n,m,head[1005],s=0,level[1005],iter[1005]; std::vector<int>v[300]; inline int addedge(const int u,const int v,const int t){ e[++cnt]=(edge){u,v,t,head[u]}; head[u]=cnt; e[++cnt]=(edge){v,u,0,head[v]}; head[v]=cnt; } std::queue<int>q; inline int min(const int a,const int b){return a<b?a:b;} void bfs(){ q.push(S); level[S]=1; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];~i;i=e[i].nxt) if(e[i].cap&&!~level[e[i].to]){ level[e[i].to]=level[u]+1; q.push(e[i].to); } } } int dfs(const int u,const int f){ if(!f||u==T)return f; for(int& i=iter[u];~i;i=e[i].nxt) if(e[i].cap&&level[e[i].to]>level[u]){ int d=dfs(e[i].to,min(f,e[i].cap)); if(d){ e[i].cap-=d; e[i^1].cap+=d; return d; } } return 0; } int dinic(){ for(int flow=0,f;;){ memset(level,-1,sizeof level); if(bfs(),!~level[T])return flow; memcpy(iter,head,sizeof head); while(f=dfs(S,0x3f3f3f3f))flow+=f; } } int main(){ memset(head,-1,sizeof head); n=readint(),m=readint(); for(int i=1;i<=n;++i){ int p=readint(); s+=p; addedge(S,i,p); } for(int i=1;i<=m;++i)addedge(n+i,T,readint()); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) addedge(i,n+j,1); if(dinic()!=s)return puts("0"),0; puts("1"); for(int i=1;i<=cnt;++i) if(e[i].from<=n&&e[i].from&&e[i].to>n&&e[i].to<=n+m&&!e[i].cap)v[e[i].from].push_back(e[i].to-n); for(int i=1;i<=n;++i) if(v[i].size()){ for(int j=v[i].size()-1;j>0;--j)printf("%d ",v[i][j]); printf("%d\n",v[i][0]); }else putchar('\n'); return 0; }