題目大意:一名老師出題,要求出N種題型,每個題型要求出多少題給出;
現在有M道題(待選問題),每道題給出它可以被歸類的題型;
問最後是否能按照要求出題,滿足要求,輸出1及每種題型對應待選問題,否則輸出0
解題策略:這題初看思路與 UVA 11045 My T-shirt suits me http://blog.csdn.net/j_dark/article/details/8830539相同,
1, 由於每種題型有容量要求,故考慮將題型size拆成題型同爲size的k道題(若該種題型要求出k道),
建立{題型拆後題,待選問題}二部圖模型;
2,判別能否按要求出題——求出待選題目相對題型的最大匹配(邏輯上可理解爲按照題型能夠出多少題),與要求出題數比較,
感覺 上述最大匹配數<=要求出題數,未證明(網上都是網絡流的報告,學過網絡流後,估計會多一種理解。)
3,用sizeMatch[i]記錄與題型拆後題匹配的待選問題,這部分如何輸出詳見註釋。
4,用鄰接矩陣練了下最大匹配算法,發現在輸出答案與運算時間方面,和鄰接表不是一個時間量級。
/*
UVA 10092 The Problem with the Problemsetter
AC by J_Dark
ON 2013/4/21
Time 0.334s 鄰接矩陣還真是慢
*/
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
const int maxn = 1005;
using namespace std;
/*
1. proSizeNum 題型數
proNum 待選問題數
proSizeSum 一共需出題數(各題型數目之和)
2. proSetNum[] 題型對應出題數
proMatch[] 與待選問題匹配的拆後題型問題
sizeMatch[] 與拆後題型問題匹配的待選問題
proMatch[]與sizeMatch[]不同!
3. 若一種題型要出第k種題,將一種題型拆成k種同題型題,建立拆後題型題——待選題二部圖
pos[i]存放第i種題型題在無向圖g中起始下標,終點爲pos[i]+proSetNum[i]
*/
int proSizeNum, proNum, proSizeSum;
int proSetNum[maxn], pos[maxn], g[maxn][maxn], proMatch[maxn], sizeMatch[maxn];
bool visited[maxn];
void Initial(){
memset(g, 0, sizeof(g));
pos[1] = 1;
for(int i=2; i<=proSizeNum; i++){
pos[i] = pos[i-1]+proSetNum[i-1];
}
}
bool findPath(int u){
for(int i=1; i<=proSizeSum; i++){
if(g[u][i] && !visited[i]){
visited[i] = true;
if(sizeMatch[i] == -1 || findPath(sizeMatch[i])){
sizeMatch[i] = u;
proMatch[u] = i;
return true;
}
}
}
return false;
}
void MaxMatch(){
for(int i=1; i<=proNum; i++) proMatch[i] = -1;
for(int i=1; i<=proSizeSum; i++) sizeMatch[i] = -1;
int maxMatch = 0;
for(int i=1; i<=proNum; i++){
if(proMatch[i] == -1){
memset(visited, false, sizeof(visited));
if(findPath(i))
maxMatch++;
}
}
//若最大匹配數即最大可以出題數與要求出題數相同,則輸出相應答案
if(maxMatch == proSizeSum){
cout << 1 << endl;
for(int i=1; i<=proSizeNum; i++){
//輸出與第i種題型匹配的proSet[i]道待選題
for(int k=0; k<proSetNum[i]; k++){
if(k>0) cout << " ";
cout << sizeMatch[pos[i]+k];
}
cout << endl;
}
}
else cout << 0 << endl;
}
///////////////////////////////////////////////////////////////
int main(){
while(cin >> proSizeNum >> proNum && (proSizeNum||proNum))
{
proSizeSum = 0;
for(int i=1; i<=proSizeNum; i++){
cin >> proSetNum[i];
proSizeSum += proSetNum[i];
}
Initial();
for(int i=1; i<=proNum; i++){
int reachedNum;
cin >> reachedNum;
for(int j=0; j<reachedNum; j++){
int v;
cin >> v;
//將一種題型v拆成k種題型爲v題
for(int k=0; k<proSetNum[v]; k++)
g[i][pos[v]+k] = v;
}
}
MaxMatch();
}
//system("pause");
return 0;
}