问题描述:
假设有来自n 个不同单位的代表参加一次国际会议。每个单位的代表数分别为
r i n i , =1,2,, 。会议餐厅共有m张餐桌,每张餐桌可容纳c (i 1,2, ,m) i = 个代表就餐。
为了使代表们充分交流,希望从同一个单位来的代表不在同一个餐桌就餐。试设计一个算法,
给出满足要求的代表就餐方案。
编程任务:
对于给定的代表数和餐桌数以及餐桌容量,编程计算满足要求的代表就餐方案。
数据输入:
由文件input.txt提供输入数据。文件第1行有2 个正整数m和n,m表示单位数,n表
示餐桌数,1<=m<=150, 1<=n<=270。文件第2 行有m个正整数,分别表示每个单位的代表
数。文件第3 行有n个正整数,分别表示每个餐桌的容量。
结果输出:
程序运行结束时,将代表就餐方案输出到文件output.txt 中。如果问题有解,在文件第
1 行输出1,否则输出0。接下来的m行给出每个单位代表的就餐桌号。如果有多个满足要
求的方案,只要输出1 个方案。
输入示例:
4 5
4 5 3 5
3 5 2 6 4
输出示例:
1
1 2 4 5
1 2 3 4 5
2 4 5
1 2 3 4 5
分析:
啊哈哈哈,这是严格意义上本人完全自己解出来的网络流题目,没有参考任何的资料和报告。小激动一下。言归正传,刚看到这题第一个感觉就是二分图,然后根据题意建模,
一个单位的人只能去不同的桌子,表示任意一个单位到任意一个桌子的路径容量是1,然后源点到所有不同的单位的容量是单位人数,所有桌子到汇点的容量是桌子人数。求出最大流看是否等于总人数,如果相等就有解,然后从每个单位到每个桌子如果有流量则输出。有木有很简单。
后来看了大牛的分析,才知道这道题目是二分图多重匹配问题。专业的模型说法是对于一个二分图,每个顶点可以有多个匹配顶点,称这类问题为二分图多重匹配问题。X,Y集合之间的边容量全部是1,保证两个点只能匹配一次(一个餐桌上只能有一个单位的一个人),源汇的连边限制了每个点匹配的个数。求出网络最大流,如果流量等于X集合所有点与S边容量之和,那么则说明X集合每个点都有完备的多重匹配。
和我的想法不谋而合啊。
另外还有贪心的解法,由于是网络流专题,这里不赘述了,请自行百度。
代码应该很清晰了,建图和求最大流的过程,感觉没什么好加注释的了。
代码:
#include<cstdio>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
const int maxn = 430;
const int INF = 1<<30;
int s,t;
struct edge{
int from,to,cap,flow;
edge(int a,int b,int c,int d):from(a),to(b),cap(c),flow(d)
{}
};
bool visit[maxn];
vector<int> g[maxn];
vector<edge> edges;
int d[maxn];
int cur[maxn];
bool BFS()
{
memset(visit,false,sizeof(visit));
d[s]=0;
queue<int> q;
q.push(s);
visit[s]=true;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<g[x].size();i++){
edge &e=edges[g[x][i]];
if(!visit[e.to]&&e.cap>e.flow){
d[e.to]=d[x]+1;
visit[e.to]=true;
q.push(e.to);
}
}
}
return visit[t];
}
int DFS(int x,int a)
{
if(x==t||a==0) return a;
int flow=0,f;
for(int &i=cur[x];i<g[x].size();i++){
edge &e=edges[g[x][i]];
if(d[e.to]==d[x]+1&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){
e.flow+=f;
flow+=f;
edges[g[x][i]^1].flow-=f;
a-=f;
if(a==0) break;
}
}
return flow;
}
void addedge(int from,int to,int cap)
{
edges.push_back(edge(from,to,cap,0));
edges.push_back(edge(to,from,0,0));
int m=edges.size();
g[from].push_back(m-2);
g[to].push_back(m-1);
}
int main()
{
int n,m;
scanf("%d%d",&m,&n);
s=0;t=m+n+1;
int temp;
int sum=0;
int max=0;
for(int i=1;i<=m;i++){
scanf("%d",&temp);
sum+=temp;
max=temp>max?temp:max;
addedge(s,i,temp);
}
for(int i=m+1;i<=n+m;i++){
scanf("%d",&temp);
addedge(i,t,temp);
}
if(max>n) {printf("0\n");return 0;}//实际测试数据中没有用
for(int i=1;i<=m;i++)
for(int j=m+1;j<=m+n;j++)
addedge(i,j,1);
int flow=0;
while(BFS()){
memset(cur,0,sizeof(cur));
flow+=DFS(s,INF);
}
if(flow==sum){
printf("1\n");
for(int i=1;i<=m;i++){
for(int j=0;j<g[i].size();j++){
edge &e=edges[g[i][j]];
if(e.flow==1) printf("%d ",e.to-m);
}
printf("\n");
}
}else printf("0\n");
return 0;
}