题意:命题人的问题,命题人被给了很多题目,每一道题目都属于某一个或几个种类,出题人要在给定的种类中选出要求数量的题目。能够选出来的打印1,并且打印每个种类选出来的书,不能选出来的打印0
分析:套用匈牙利算法,重点在于构造二部图,参照uva10080 Gopher II,将每一道题看做一个gopher,每个种类看做相同的几个洞,如果A类型需要选三道,那么就将A类型看做三个洞,编号为0 、 1 、 2.那么如果有一道题归为A类的话,在连接表或者链接矩阵中就要和0 、 1、 2都有联系。
WA经验:问题总是处在初始化link 和 visited 上,对于小数字量与会发觉,要么给到很大的范围,要么清楚地知道link 、visited 分别对于谁而言,下标范围要相应的变化。
代码:
//uva10092The Problem with the Problemsetter
//AC By Warteac
//2013-4-20
//Runtime:0.152s
#include<iostream>
#include<cmath>
#include<vector>
#include<cstring>
using namespace std;
class Problemsetter{
private:
vector < vector<int> > selectProb;//邻接表
int numberOfCategories;//3
int numberOfProblems;//15
int aimNumOfProb;//3+3+4
int numOfMatch;
vector <int> proNumOfEachCate;//3 3 4
vector <int> firstIndexOfCate;
private:
vector <int> link;
vector <bool> visited;
bool dfs(int u);
public:
void initial(int,int);
void readCase();
void computing();
void outResult();
};
void Problemsetter::initial(int n,int m){
numberOfCategories = n;
numberOfProblems = m;
selectProb.clear();
proNumOfEachCate.clear();
proNumOfEachCate.push_back(0);//下标从1开始
firstIndexOfCate.clear();
firstIndexOfCate.push_back(0);//下标从1开始
aimNumOfProb = 0;
numOfMatch = 0;
link.clear();
visited.clear();
}
void Problemsetter::readCase(){
int n = numberOfCategories;
int m = numberOfProblems;
int temp,cate;
vector <int> category;
while(n--){
cin >> temp;
aimNumOfProb += temp;
firstIndexOfCate.push_back(firstIndexOfCate[firstIndexOfCate.size()-1]+proNumOfEachCate[proNumOfEachCate.size()-1]);
proNumOfEachCate.push_back(temp);
}
while(m--){
cin >> cate;
category.clear();
while(cate--){
cin >> temp;
//将每本书可以归到每一类:第一本归到第一类3次,分别编号从0到2,归到第二类3次,编号从3到5
for(int i = 0; i < proNumOfEachCate[temp]; i++)
category.push_back(firstIndexOfCate[temp]+i);
}
//将每本书的记录加入表中,每一本书是一行记录,下标从0开始
selectProb.push_back(category);
}
}
bool Problemsetter::dfs(int u){//visited ,link 的初始化,link的初始化只需要一次,visited每DFS一次都要初始化
for(int i = 0; i < selectProb[u].size(); i++){
//cout << "j = " << i <<endl;
int c = selectProb[u][i];//cout << "c = " << c<<endl;
if(!visited[c]){ //visited[i]-WA,dfs递归调用时就会出错了
visited[c] = true;
if( (link[c] == -1 ) || dfs(link[c])==true ){
link[c] = u;
return true;
}
}
}
return false;
}
void Problemsetter::computing(){//hungary
for(int i = 0; i < aimNumOfProb; i++){//initial link
link.push_back(-1);
}
for(int i = 0; i < selectProb.size(); i++){
//cout << "i = " <<i<<endl;
visited.clear();
visited.resize(aimNumOfProb);
if(dfs(i)) numOfMatch++;
}
}
void Problemsetter::outResult(){
/*for(int x = 0 ; x < selectProb.size(); x++ ){
for(int y = 0; y < selectProb[x].size(); y++){
cout << selectProb[x][y] << " ";
}
cout << endl;
}*/
if(numOfMatch == aimNumOfProb){
cout << "1" <<endl;
for(int x = 1; x <= numberOfCategories; x++){
int t = 0;
for (int i = 0; i < link.size(); i++){
if( i >= firstIndexOfCate[x] && i < (firstIndexOfCate[x] + proNumOfEachCate[x])){
if(t) cout << " "; t++;
cout << link[i]+1;
}
}
cout <<endl;
}
}else{ cout << "0" <<endl;}
//cout <<"aimNumOfProb = "<<aimNumOfProb<<endl;
}
int main(){
Problemsetter p;
int n,m;
while(cin >> n >> m && n && m){
p.initial(n,m);
p.readCase();
p.computing();
p.outResult();
}
}