AcWing 406. 放置机器人

大型补档计划

题目链接

预处理每个列、行连续块。
每个每个列行只能在一个位置匹配,否则冲突。
符合二分图性质,跑匈牙利即可。

点数最坏情况 (N * M) (墙空地相间分布),边数最坏情况 (N * M) (每个点最多会加一条边),所以复杂度 (O(N ^ 2M ^ 2)) 跑得动

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 55;
int n, m, c1, c2, r[N][N], c[N][N], match[N * N];
char g[N][N];
int head[N * N], numE = 0;
bool vis[N * N];
struct E{
	int next, v;
} e[N * N];
void add(int u, int v) {
	e[++numE] = (E) { head[u], v };
	head[u] = numE;
}
bool find(int u) {
	for (int i = head[u]; i; i = e[i].next) {
		int v = e[i].v;
		if (vis[v]) continue;
		vis[v] = true;
		if (!match[v] || find(match[v])) {
			match[v] = u; return true;
		}
	}
	return false;
}
int main() {
	int T; scanf("%d", &T);
	for (int t = 1; t <= T; t++) {
	    numE = 0;
	    memset(match, 0, sizeof match);
	    memset(head, 0, sizeof head);
		scanf("%d%d", &n, &m); c1 = c2 = 0;
		for (int i = 1; i <= n; i++) scanf("%s", g[i] + 1);
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				if (g[i][j] != '#') {
					if (j > 1 && g[i][j - 1] != '#') r[i][j] = r[i][j - 1];
					else r[i][j] = ++c1;
					if (i > 1 && g[i - 1][j] != '#') c[i][j] = c[i - 1][j];
					else c[i][j] = ++c2;
				}
			}
		}	
		for (int i = 1; i <= n; i++) 
			for (int j = 1; j <= m; j++)
				if (g[i][j] == 'o') add(r[i][j], c[i][j]);
		int ans = 0;
		for (int i = 1; i <= c1; i++) {
			memset(vis, false, sizeof vis);
			if (find(i)) ans++;
		}
		printf("Case :%d
%d
", t, ans);
	}
	
}
原文地址:https://www.cnblogs.com/dmoransky/p/12380709.html