hdu 1045 Fire Net————二分图匹配(匈牙利算法)

Suppose that we have a square city with straight streets. A map of a city is a square board with n rows and n columns, each representing a street or a piece of wall.

A blockhouse is a small castle that has four openings through which to shoot. The four openings are facing North, East, South, and West, respectively. There will be one machine gun shooting through each opening.

Here we assume that a bullet is so powerful that it can run across any distance and destroy a blockhouse on its way. On the other hand, a wall is so strongly built that can stop the bullets.

The goal is to place as many blockhouses in a city as possible so that no two can destroy each other. A configuration of blockhouses is legal provided that no two blockhouses are on the same horizontal row or vertical column in a map unless there is at least one wall separating them. In this problem we will consider small square cities (at most 4x4) that contain walls through which bullets cannot run through.

The following image shows five pictures of the same board. The first picture is the empty board, the second and third pictures show legal configurations, and the fourth and fifth pictures show illegal configurations. For this board, the maximum number of blockhouses in a legal configuration is 5; the second picture shows one way to do it, but there are several other ways.



Your task is to write a program that, given a description of a map, calculates the maximum number of blockhouses that can be placed in the city in a legal configuration.

Input

The input file contains one or more map descriptions, followed by a line containing the number 0 that signals the end of the file. Each map description begins with a line containing a positive integer n that is the size of the city; n will be at most 4. The next n lines each describe one row of the map, with a '.' indicating an open space and an uppercase 'X' indicating a wall. There are no spaces in the input file.

Output

For each test case, output one line containing the maximum number of blockhouses that can be placed in the city in a legal configuration.

Sample Input

4
.X..
....
XX..
....
2
XX
.X
3
.X.
X.X
.X.
3
...
.XX
.XX
4
....
....
....
....
0

Sample Output

5
1
5
2
4

 

思路:

在有墙的地图上放置小房子,要求小房子不能有两个同时在一行或一列,除非他们之间有墙。

这道题可以dfs,但是本次讲用如何用二分图匹配做。

首先,我们考虑应该如何建立这个二分图。

我们需要分析这道题与二分图之间的联系。

这道题要求最多的“房子”,而二分图最大匹配求得正是最多的“边”,所以这题思路很有可能是将 房子 等同于边。

我们从没有墙的情况下开始分析,

对于每个点,如果放置了一个小房子,那么这一行和这一列的点都不能再放置房子了。

这正如在一个二分图中,选择了一条边,左边的节点和右边的节点都不能再选了。

就像,如果你在(1,1)位置放置了房子,那么直接排除所有第一列和第一行的位置。

所以对应于二分图,需要有选(1,1)这条边,就会排除与左节点1和右节点1的连线的效果。

所以左节点行号,右节点就是列号。每条边,代表每个位置,选了就是放置了房子。将所有节点连线后:

我们如果选择(1,1)了这条边(绿色),然后就会排除一些边(排除的边是红色):

最后求出的最大匹配,就是答案。

那么,如果有墙的话,原理和没墙其实差不多。还是选择一个位置排除一些位置,对应于二分图,还是选择一条边,然后排除另一些边。

我们要先将图预处理一下,将它按分成在横行上相冲突的区域,和在竖行上相冲突的区域,如图:

                            图一                                                                           图二

我们,还是观察,如果选择了(1,1)这个格子,那么对应的,排除了图一中有1的位置和图二中有1的位置。

如果选择了(1,3)这个格子,那么对应的,排除了图一中有2的位置和图二中有4的位置。

所以,对应二分图中的两部分的节点应该代表的是图一中的区域号,和图二中的区域号,每个点的代表一个边,左右连得节点号就是图一图二对应的区域号。

选择(1,3)这个格子,就代表选了一条 2 —— 4 的边,排除了所有连着左2和右4的边(如图)。

 

(另外:这道题与 POJ 2226 Muddy Fields 的解法几乎一样的,甚至将它的代码粘过来改个输入就能ac,但是思想不同。)

 

AC代码:

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>

#define MAXN 205
#define INF 0x3f3f3f3f

using namespace std;

int r, c, n;
int xx[MAXN][MAXN], yy[MAXN][MAXN], match[MAXN * MAXN];
char a[MAXN][MAXN];
bool edge[MAXN][MAXN], vis[MAXN * MAXN];

int getxy() {
	int tx = 1, ty = 1;
	for (int i = 1; i <= r; i++) {
		for (int j = 1; j <= c+1; j++) {
			if (a[i][j] == '.') {
				xx[i][j] = tx;
			}
			else if (a[i][j - 1] == '.') {
				tx++;
			}
		}
	}
	for (int i = 1; i <= c; i++) {
		for (int j = 1; j <= r + 1; j++) {
			if (a[j][i] == '.') {
				yy[j][i] = ty;
			}
			else if (a[j-1][i] == '.') {
				ty++;
			}
		}
	}
	return max(tx, ty);
}

int dfs(int u) {
	for (int i = 1; i <= n; i++) {
		if (vis[i] == false && edge[u][i] == true) {
			vis[i] = true;
			if (match[i] == 0 || dfs(match[i])) {
				match[i] = u;
				return 1;
			}
		}
	}
	return 0;
}

int main() {
	
	while (scanf("%d", &r) != EOF && r) {
		memset(xx, 0, sizeof(xx));
		memset(yy, 0, sizeof(yy));
		memset(edge, false, sizeof(edge));
		memset(match, 0, sizeof(match));
		memset(a, 'X', sizeof(a));
		c = r;
		getchar();
		for (int i = 1; i <= r; i++) {
			for (int j = 1; j <= c; j++) {
				scanf("%c", &a[i][j]);
			}
			getchar();
		}
		n = getxy();
		for (int i = 1; i <= r; i++) {
			for (int j = 1; j <= c; j++) {
				edge[xx[i][j]][yy[i][j]] = true;
			}
		}
		int sum = 0;
		for (int i = 1; i <= n; i++) {
			memset(vis, false, sizeof(vis));
			if (dfs(i)) {
				sum++;
			}
		}
		printf("%d\n", sum);
	}
	

	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值