Luogu P1263 宫廷守卫

题目传送门

这守卫怕不是铁憨憨,互相\(TK\)欢乐多


如果没有墙的话,可以将它的行和列分别看成点,对于每一块空地\((i, j)\),在\(i\)\(j\)之间连一条边,然后跑二分图匹配就好了
然后考虑墙的影响,墙会把一行或一列分成几段互不影响的区间,将这几段区间的行(列)标上不同号,表示它们互不影响,然后再跑二分图匹配就好了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
LL read() {
	LL k = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9')
		k = k * 10 + c - 48, c = getchar();
	return k * f;
}
int mapp[210][210];
struct zzz {
	int t, nex;
}e[40010 << 2]; int head[40010], tot;
void add(int x, int y) {
	e[++tot].t = y;
	e[tot].nex = head[x];
	head[x] = tot;
}
int pos[210][210], pos2[210][210], cnt, cnt2;
bool vis[40010]; int lin[40010];
bool findd(int x) {
	for(int i = head[x]; i; i = e[i].nex)
		if(!vis[e[i].t]) {
			vis[e[i].t] = 1;
			if(!lin[e[i].t] || findd(lin[e[i].t])) {
				lin[e[i].t] = x; return 1;
			}
		}
	return 0;
}
int main() {
	int n = read(), m = read();
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			mapp[i][j] = read();
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			if(mapp[i][j] != 2)
				if(mapp[i][j-1] == 2 || !(j-1)) pos[i][j] = ++cnt;
				else pos[i][j] = pos[i][j-1];
	for(int j = 1; j <= m; ++j)
		for(int i = 1; i <= n; ++i)
			if(mapp[i][j] != 2)
				if(mapp[i-1][j] == 2 || !(i-1)) pos2[i][j] = ++cnt2;
				else pos2[i][j] = pos2[i-1][j];
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			if(mapp[i][j] == 0) add(pos[i][j], pos2[i][j]);
	int ans = 0;
	for(int i = 1; i <= cnt; ++i) {
		for(int j = 1; j <= cnt2; ++j) vis[j] = 0;
		ans += findd(i);
	}
	printf("%d\n", ans);
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j) {
			if(!mapp[i][j] && pos[i][j] == lin[pos2[i][j]]) printf("%d %d\n", i, j);
		}
	return 0;
}
posted @ 2019-11-14 11:21  MorsLin  阅读(92)  评论(0编辑  收藏  举报