問題描述:
假設有來自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;
}