「ZOJ 1654」Place the Robots

Description

有一个 (N imes M) 的棋盘,棋盘的每一格是三种类型之一:空地、草地、墙。机器人只能放在空地上。在同一行或同一列的两个机器人,若它们之间没有墙,则它们可以互相攻击。问给定的棋盘,最多可以放置多少个机器人,使它们不能互相攻击。

Hint

(1le N, Mle 50)

Solution

我们将 每一行、每一列被 隔开,并且包含 空地连续区域 称作“块”。

假如地图是这样子的:

YCjsz9.png

注:图源自 XJ 课件,下同。

那么两种分割块的方式为:

YCxZHP.png

图中数字为块的编号。显然一个块中最多只能放一个机器人。

如果我们将块视作点,如果横块于竖块相交于一个 空地,那么将其连边。

最后我们可以得到这么一张图:

YCzWd0.png

上面是横块,下面是竖块。容易发现最终构成的是一张二分图。

由于空地对于的是边,所以 放置机器人就相当于选择边 。而块对应点,一个块只能放一个机器人,因此 一个点相邻的最多只允许一条边被选择

于是,二分图最大匹配。匈牙利算法即可,复杂度 (O(N imes M))

Code

/*
 * Author : _Wallace_
 * Source : https://www.cnblogs.com/-Wallace-/
 * Problem : ZOJ 1654 Place the Robots
 */
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>

using namespace std;
const int N = 55;
char str[N][N];
int n, m;

int tag[2][N][N];
int cnt[2];

const int dx[4] = {0, 0, 1, -1};
const int dy[4] = {1, -1, 0, 0};
const int dt[2][2] = {{0, 1}, {2, 3}};
void fill(int t, int x, int y, int c) {
	tag[t][x][y] = c;
	for (register int i = 0; i < 2; i++) {
		x += dx[dt[t][i]], y += dy[dt[t][i]];
		if (x < 1 || x > n || y < 1 || y > m) continue;
		if (tag[t][x][y] || str[x][y] == '#') continue;
		fill(t, x, y, c);
		x -= dx[dt[t][i]], y -= dy[dt[t][i]];
	}
}

vector<int> G[N * N];
inline void connect(int x, int y) {
	G[x].push_back(y);
}

int match[N * N], vis[N * N];
bool Dfs(int x, int t) {
	if (vis[x] == t) return false;
	vis[x] = t;
	for (vector<int>::iterator y = G[x].begin(); y != G[x].end(); y++)
		if (!match[*y] || Dfs(match[*y], t))
			return match[*y] = x, true;
	return false;
}

int solve() {
	memset(tag, 0, sizeof tag);
	memset(cnt, 0, sizeof cnt);
	for (register int t = 0; t < 2; t++)
		for (register int i = 1; i <= n; i++)
			for (register int j = 1; j <= m; j++)
				if (str[i][j] == 'o')
					if (!tag[t][i][j])
						fill(t, i, j, ++cnt[t]);
	
	for (register int i = 1; i <= cnt[0]; i++)
		G[i].clear();
	for (register int i = 1; i <= n; i++)
		for (register int j = 1; j <= m; j++)
			if (str[i][j] == 'o')
				connect(tag[0][i][j], tag[1][i][j]);
	
	memset(vis, 0, sizeof vis);
	memset(match, 0, sizeof match);
	int total = 0;
	for (register int i = 1; i <= cnt[0]; i++)
		total += int(Dfs(i, i));
	return total;
}

signed main() {
	int T; scanf("%d", &T);
	for (register int i = 1; i <= T; i++) {
		printf("Case :%d
", i);
		scanf("%d%d", &n, &m);
		for (register int i = 1; i <= n; i++)
			scanf("%s", str[i] + 1);
		printf("%d
", solve());
	}
}
原文地址:https://www.cnblogs.com/-Wallace-/p/12828324.html