题目大意:一名老师出题,要求出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;
}