1. 程式人生 > >【POJ - 2226】Muddy Fields(匈牙利演算法 或 網路流dinic,二分圖匹配,最小點覆蓋,矩陣中優秀的建圖方式 )

【POJ - 2226】Muddy Fields(匈牙利演算法 或 網路流dinic,二分圖匹配,最小點覆蓋,矩陣中優秀的建圖方式 )

題幹:

Rain has pummeled the cows' field, a rectangular grid of R rows and C columns (1 <= R <= 50, 1 <= C <= 50). While good for the grass, the rain makes some patches of bare earth quite muddy. The cows, being meticulous grazers, don't want to get their hooves dirty while they eat. 

To prevent those muddy hooves, Farmer John will place a number of wooden boards over the muddy parts of the cows' field. Each of the boards is 1 unit wide, and can be any length long. Each board must be aligned parallel to one of the sides of the field. 

Farmer John wishes to minimize the number of boards needed to cover the muddy spots, some of which might require more than one board to cover. The boards may not cover any grass and deprive the cows of grazing area but they can overlap each other. 

Compute the minimum number of boards FJ requires to cover all the mud in the field.

Input

* Line 1: Two space-separated integers: R and C 

* Lines 2..R+1: Each line contains a string of C characters, with '*' representing a muddy patch, and '.' representing a grassy patch. No spaces are present.

Output

* Line 1: A single integer representing the number of boards FJ needs.

Sample Input

4 4
*.*.
.***
***.
..*.

Sample Output

4

Hint

OUTPUT DETAILS: 

Boards 1, 2, 3 and 4 are placed as follows: 
1.2. 
.333 
444. 
..2. 
Board 2 overlaps boards 3 and 4.

題目大意:

如下圖告訴你一個矩陣,讓你用1 * x (x 為任意值) 的木板去鋪符號*  可以覆蓋

問最少多少木板能夠把所有的*覆蓋掉

解題報告:

    這應該是自ACM入坑以來做的最接近網路流的一道題,回顧這半年多的題海中的掙扎,現在碰觸到高階演算法的邊緣了,感慨萬千啊!話不多說,這題重點和難點在於建圖。

首先,這種型別的大概就是二分圖的最小點覆蓋或者最小邊覆蓋,建圖的方式就是:左側集合表示行的連通塊,右側集合表示列的連通塊,這樣如果左側和右側有條邊,說明這個格子(單蹦 或者是 連通塊的交點)是需要被覆蓋的,於是乎所有的 ' * ' ,都被蘊含在這些邊中,所以我們只需要找一些連通塊,也就是找一些點,來使所有的邊都與之有聯絡,就可以了。又因為要求最小的木板個數,也就是求最小點覆蓋集。又因為二分圖中 最小點覆蓋集 = 最大匹配 ,所以匈牙利就搞定了。

另:這裡是巧妙用了vis1和vis2來記錄連通塊標號,所以一個for迴圈就都處理完了,但是如果用if(maze[i][j-1]=='*')這麼來判斷,那就最好先處理vis1,再vis2,寫兩次for迴圈,這樣不容易亂。

(ps:定義陣列的時候MAX*MAX的,多加個 / 2,就226ms變到63ms、、、恐怖啊)

附圖:

另外,鄰接表可以優化到0ms。。。。

AC程式碼:

#include<bits/stdc++.h> 
using namespace std;

const int MAX = 55;
int line[MAX * MAX][MAX * MAX];
int nxt[MAX * MAX];
bool used[MAX * MAX];
char maze[MAX][MAX];
int vis1[MAX][MAX], vis2[MAX][MAX];
int n,m,tot1,tot2;
bool find(int u) {
	for(int i = 1; i <= tot2; i++) {
		if(line[u][i] && !used[i]) {
			used[i] = 1;
			if(nxt[i] == -1 || find(nxt[i])) {
				nxt[i] = u;
				return true;
			}
		}
	}
	return false;
}
int match() {
	int cnt = 0;
	memset(nxt, -1, sizeof(nxt));
	for(int i = 1; i <= tot1; i++) {
		memset(used, 0, sizeof(used));
		if(find(i)) cnt++;
	}
	return cnt;
}
int main() 
{
	while(~scanf("%d%d",&n,&m)) {
		for(int i = 1; i <= n; i++) {
			scanf("%s",maze[i]+1); 
		}
		tot1=tot2=0;
		memset(vis1,0,sizeof(vis1));
		memset(vis2,0,sizeof(vis2));
		memset(line,0,sizeof(line));
		for(int i = 1; i <= n; i++) {
			for(int j = 1; j <= m; j++) {
				if(maze[i][j] == '*') {
					if(!vis1[i][j - 1]) vis1[i][j] = ++tot1;
					else vis1[i][j] = vis1[i][j - 1];
					if(!vis2[i - 1][j]) vis2[i][j] = ++tot2;
					else vis2[i][j] = vis2[i - 1][j];
				}
			}
		}
		for(int i = 1; i <= n; i++) {
			for(int j = 1; j <= m; j++) {
				if(maze[i][j] == '*') {
					line[vis1[i][j]][vis2[i][j]] = 1;
//					printf("**%d %d %d %d\n",i,j,vis1[i][j],vis2[i][j]);
				}
			}
		}
		printf("%d\n",match());
	}
	return 0;
}
/*

2 2
**
.*

*/

網路流的解法看:題解