1. 程式人生 > >1281 (二部圖匹配問題)

1281 (二部圖匹配問題)

小希和Gardon在玩一個遊戲:對一個N*M的棋盤,在格子裡放盡量多的一些國際象棋裡面的“車”,並且使得他們不能互相攻擊,這當然很簡單,但是Gardon限制了只有某些格子才可以放,小希還是很輕鬆的解決了這個問題(見下圖)注意不能放車的地方不影響車的互相攻擊。 
所以現在Gardon想讓小希來解決一個更難的問題,在保證儘量多的“車”的前提下,棋盤裡有些格子是可以避開的,也就是說,不在這些格子上放車,也可以保證儘量多的“車”被放下。但是某些格子若不放子,就無法保證放盡量多的“車”,這樣的格子被稱做重要點。Gardon想讓小希算出有多少個這樣的重要點,你能解決這個問題麼? 

Input

輸入包含多組資料, 
第一行有三個數N、M、K(1<N,M<=100 1<K<=N*M),表示了棋盤的高、寬,以及可以放“車”的格子數目。接下來的K行描述了所有格子的資訊:每行兩個數X和Y,表示了這個格子在棋盤中的位置。 

Output

對輸入的每組資料,按照如下格式輸出: 
Board T have C important blanks for L chessmen. 

Sample Input

3 3 4
1 2
1 3
2 1
2 2
3 3 4
1 2
1 3
2 1
3 2

Sample Output

Board 1 have 0 important blanks for 2 chessmen.
Board 2 have 3 important blanks for 3 chessmen.

題意 :必要點:若這個點不放棋子時,就不會達到放的最多的棋子數;找出有多少個這樣的點;

思路:剛看到這個題時,不知道怎做;原來是二部圖匹配,把 座標(x,y) x ,y 各看為二部圖的一方,先找出最大匹配,再判斷去掉一些點,是不是還是最大匹配,若不是,就是必要點, 若 還是最大匹配的話,就不是必要點;

程式碼:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#include<vector>
#define Max 110

vector<int> v[Max];
int mpp[Max][Max],book[Max],match[Max];
int n,m,k;

void init()
{
	memset(match,-1,sizeof(match));
	memset(mpp,0,sizeof(mpp));
	for(int i = 0;i<=n;i++)
		v[i].clear();
}
int dfs(int u)                      // 匈牙利演算法;  
{
	
	for(int i = 0;i<v[u].size();i++)
	{
		int k = v[u][i];            // book[i] 看看這個深搜,右邊的位置上i點是否搜過; 
		if(!book[k]&&mpp[u][k])     // mpp[][]陣列的作用就是在 去掉這個點時,判斷最大匹配數的時的個數是否和不去時相等;          
		{							
			book[k] = 1;	        // 單向邊,標記的是v不是u;
			int t = match[k];               
			if(t < 0||dfs(t))
			{
				match[k] = u;      // 最大匹配分為兩部分,當左邊的找右邊的時,單向邊 
				return 1;          	// 只讓右邊數存左邊的匹配的位置;自己想想; 
			}
		} 
	}
	return 0;
}
int find()                // 找二部圖最大匹配; 
{
	int i,j;
	int res = 0;
	for(i = 1;i<=n;i++)    // 左邊的點; 
	{
		memset(book,0,sizeof(book));
		if(dfs(i))       // 去找右邊的點匹配; 
			res++;
	}
	return res;
}

int main()
{
	int i,j;
	int num = 1;
	while(~scanf("%d%d%d",&n,&m,&k))
	{
		int x,y;
		init();
		for(i = 0;i<k;i++)
		{
			scanf("%d%d",&x,&y);
			mpp[x][y] = 1;        // 單向邊; 
			v[x].push_back(y);     // 存邊; 
		}
		int mx_p = find();        // 找出最大匹配數 
		int res = 0;
		for(i = 1;i<=n;i++)
		{
			for(j = 1;j<=m;j++)
			{
				if(mpp[i][j])
				{
					memset(match,-1,sizeof(match));   // 每找一次最大匹配要把匹配陣列初始化; 
					mpp[i][j] = 0;                    // 先去掉這個點,判斷與最大匹配是否相同; 
					int tt=find(); 
					if(tt!= mx_p)                     // 若不一樣就說明這個點一定能要放棋子; 
						res++;	
					mpp[i][j] = 1;    
				}
			}
		}
		printf("Board %d have %d important blanks for %d chessmen.\n",num++,res,mx_p);
	}
	return 0;
}