【POJ - 2226】Muddy Fields(匈牙利算法 或 网络流dinic,二分图匹配,最小点覆盖,矩阵中优秀的建图方式 )

版权声明:欢迎学习我的博客,希望ACM的发展越来越好~ https://blog.csdn.net/qq_41289920/article/details/83064507

题干:

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
**
.*

*/

网络流的解法看:题解

猜你喜欢

转载自blog.csdn.net/qq_41289920/article/details/83064507