圆桌问题(最大流)

圆桌问题

https://www.luogu.org/problemnew/show/P3254

题目描述

假设有来自m 个不同单位的代表参加一次国际会议。每个单位的代表数分别为ri (i =1,2,……,m)。

会议餐厅共有n 张餐桌,每张餐桌可容纳ci (i =1,2,……,n)个代表就餐。

为了使代表们充分交流,希望从同一个单位来的代表不在同一个餐桌就餐。试设计一个算法,给出满足要求的代表就餐方案。

对于给定的代表数和餐桌数以及餐桌容量,编程计算满足要求的代表就餐方案。

输入输出格式

输入格式:

第1 行有2 个正整数m 和n,m 表示单位数,n 表示餐桌数,1<=m<=150, 1<=n<=270。

第2 行有m 个正整数,分别表示每个单位的代表数。

第3 行有n 个正整数,分别表示每个餐桌的容量。

输出格式:

如果问题有解,第1 行输出1,否则输出0。接下来的m 行给出每个单位代表的就餐桌号。如果有多个满足要求的方案,只要输出1 个方案。

输入输出样例

输入样例#1: 
4 5
4 5 3 5
3 5 2 6 4
输出样例#1: 
1
1 2 4 5
1 2 3 4 5
2 4 5
1 2 3 4 5

源点拉一条容量为aa[i]的边到左边的点,左边的点拉容量为1的边到右边的点,右边的点拉容量为bb[i]的边到汇点
  1 #include<iostream>
  2 #include<cstring>
  3 #include<string>
  4 #include<cmath>
  5 #include<cstdio>
  6 #include<algorithm>
  7 #include<queue>
  8 #include<vector>
  9 #include<set>
 10 #define maxn 1000005
 11 #define MAXN 1000005
 12 #define mem(a,b) memset(a,b,sizeof(a))
 13 const int N=1000005;
 14 const int M=1000005;
 15 const int INF=0x3f3f3f3f;
 16 using namespace std;
 17 int n;
 18 struct Edge{
 19     int v,next;
 20     int cap,flow;
 21 }edge[MAXN*6];//注意这里要开的够大。。不然WA在这里真的想骂人。。问题是还不报RE。。
 22 int cur[MAXN],pre[MAXN],gap[MAXN],path[MAXN],dep[MAXN];
 23 int cnt=0;//实际存储总边数
 24 void isap_init()
 25 {
 26     cnt=0;
 27     memset(pre,-1,sizeof(pre));
 28 }
 29 void isap_add(int u,int v,int w)//加边
 30 {
 31     edge[cnt].v=v;
 32     edge[cnt].cap=w;
 33     edge[cnt].flow=0;
 34     edge[cnt].next=pre[u];
 35     pre[u]=cnt++;
 36 }
 37 void add(int u,int v,int w){
 38     isap_add(u,v,w);
 39     isap_add(v,u,0);
 40 }
 41 bool bfs(int s,int t)//其实这个bfs可以融合到下面的迭代里,但是好像是时间要长
 42 {
 43     memset(dep,-1,sizeof(dep));
 44     memset(gap,0,sizeof(gap));
 45     gap[0]=1;
 46     dep[t]=0;
 47     queue<int>q;
 48     while(!q.empty())
 49     q.pop();
 50     q.push(t);//从汇点开始反向建层次图
 51     while(!q.empty())
 52     {
 53         int u=q.front();
 54         q.pop();
 55         for(int i=pre[u];i!=-1;i=edge[i].next)
 56         {
 57             int v=edge[i].v;
 58             if(dep[v]==-1&&edge[i^1].cap>edge[i^1].flow)//注意是从汇点反向bfs,但应该判断正向弧的余量
 59             {
 60                 dep[v]=dep[u]+1;
 61                 gap[dep[v]]++;
 62                 q.push(v);
 63                 if(v==s)//感觉这两句优化加了一般没错,但是有的题可能会错,所以还是注释出来,到时候视情况而定
 64                 break;
 65             }
 66         }
 67     }
 68     return dep[s]!=-1;
 69 }
 70 int isap(int s,int t)
 71 {
 72     if(!bfs(s,t))
 73     return 0;
 74     memcpy(cur,pre,sizeof(pre));
 75     //for(int i=1;i<=n;i++)
 76     //cout<<"cur "<<cur[i]<<endl;
 77     int u=s;
 78     path[u]=-1;
 79     int ans=0;
 80     while(dep[s]<n)//迭代寻找增广路,n为节点数
 81     {
 82         if(u==t)
 83         {
 84             int f=INF;
 85             for(int i=path[u];i!=-1;i=path[edge[i^1].v])//修改找到的增广路
 86                 f=min(f,edge[i].cap-edge[i].flow);
 87             for(int i=path[u];i!=-1;i=path[edge[i^1].v])
 88             {
 89                 edge[i].flow+=f;
 90                 edge[i^1].flow-=f;
 91             }
 92             ans+=f;
 93             u=s;
 94             continue;
 95         }
 96         bool flag=false;
 97         int v;
 98         for(int i=cur[u];i!=-1;i=edge[i].next)
 99         {
100             v=edge[i].v;
101             if(dep[v]+1==dep[u]&&edge[i].cap-edge[i].flow)
102             {
103                 cur[u]=path[v]=i;//当前弧优化
104                 flag=true;
105                 break;
106             }
107         }
108         if(flag)
109         {
110             u=v;
111             continue;
112         }
113         int x=n;
114         if(!(--gap[dep[u]]))return ans;//gap优化
115         for(int i=pre[u];i!=-1;i=edge[i].next)
116         {
117             if(edge[i].cap-edge[i].flow&&dep[edge[i].v]<x)
118             {
119                 x=dep[edge[i].v];
120                 cur[u]=i;//常数优化
121             }
122         }
123         dep[u]=x+1;
124         gap[dep[u]]++;
125         if(u!=s)//当前点没有增广路则后退一个点
126         u=edge[path[u]^1].v;
127      }
128      return ans;
129 }
130 
131 int aa[350];
132 int bb[350];
133 
134 int main(){
135     int m,s,t;
136     cin>>m>>n;
137     int a,b,c;
138     isap_init();
139     int sum=0;
140     for(int i=1;i<=m;i++){
141         cin>>aa[i];///代表数量
142         sum+=aa[i];
143     }
144     for(int i=1;i<=n;i++){
145         cin>>bb[i];///桌的容量
146     }
147     for(int i=1;i<=m;i++){
148         for(int j=1;j<=n;j++){
149             add(i,m+j,1);
150         }
151     }
152     s=0,t=n+m+1;
153     for(int i=1;i<=m;i++){
154         add(s,i,aa[i]);
155     }
156     for(int i=1;i<=n;i++){
157         add(m+i,t,bb[i]);
158     }
159     n=n+m+2;
160     int ans=isap(s,t);
161     if(ans==sum){
162         cout<<1<<endl;
163         for(int i=1;i<=m;i++){
164             for(int j=pre[i];~j;j=edge[j].next){
165                 if(edge[j].flow==1){
166                     cout<<edge[j].v-m<<" ";
167                 }
168             }
169             cout<<endl;
170         }
171     }
172     else{
173         cout<<0<<endl;
174     }
175 }
View Code

猜你喜欢

转载自www.cnblogs.com/Fighting-sh/p/9829657.html