UVA 10092 The Problem with the Problemsetter【二分圖最大匹配變形 鄰接矩陣實現最大匹配算法】

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章