【代碼超詳解】HDU 1045 Fire Net(建圖 + 二分圖匹配)

一、題目描述

在這裏插入圖片描述樣例輸入:

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

樣例輸出:

5
1
5
2
4

二、算法分析說明與代碼編寫指導

本題的難點在於如何建立正確的圖論模型。
一行、一列同時只能有一個炮,除非被牆擋住。炮彈向上下左右發射後,直到被牆擋住之前,會摧毀經過的路徑上的任何物體。
依次按照行優先或列優先的方式爲一行或一列遇到牆或行 / 列末端之前的一組點編同一個號碼(縮點建圖),然後以行列的編號爲頂點建立二分圖並作二分圖匹配即可。
例如樣例中的第一個圖

.X..
....
XX..
....

的行優先或列優先的編號結果及相應的二分圖中的所有邊如下(規定按列優先的編號存入二分圖的時候要加 8,不難發現當城市地圖不超過 4 × 4 時編號最大就爲 8):
在這裏插入圖片描述
這樣建圖的原理是:在一個空位放置一門炮以後,從該點出發,同一行同一列遇到牆之前的任何位置都不能再放。這一條約束對應二分圖匹配的性質:每一個左側的點都只能在右側任選一個點匹配。在不遇到牆的情況下,同一行放了兩門炮對應左側一個點連了右側兩個點;同一列放了兩門炮對應左側兩個點連了右側一個點。這兩種情況在匹配的過程都是不允許出現的。

三、AC 代碼

#include<cstdio>
#include<bitset>
#include<vector>
#pragma warning(disable:4996)
using namespace std;
const unsigned off = 8;
bitset<17> g[17], vis; unsigned n, m1[5][5], m2[5][5], c, match[17]; char M[7][7], a;
bool dfs(const unsigned& u) {
	unsigned v;
	for (unsigned v = 1; v <= 16; ++v) {
		if (vis[v] || !g[u][v])continue;
		vis[v] = true;
		if (!match[v] || dfs(match[v])) { match[v] = u; match[u] = v; return true; }
	}
	return false;
}
inline void test() {
	puts("TEST");
	for (unsigned i = 1; i <= n; ++i) {
		for (unsigned j = 1; j <= n; ++j)printf("%u ", m1[i][j]);
		putchar('\t');
		for (unsigned j = 1; j <= n; ++j)printf("%u ", m2[i][j]);
		putchar('\n');
	}
	puts("END TEST");
}
int main() {
	for (;;) {
		scanf("%u", &n); if (n == 0)return 0;
		getchar(); fill(match + 1, match + 17, 0); a = 0;
		for (unsigned i = 1; i <= n; ++i) {
			fill(M[i] + 1, M[i] + 1 + n, 0);
			fill(m1[i] + 1, m1[i] + 1 + n, 0); fill(m2[i] + 1, m2[i] + 1 + n, 0);
		}
		for (unsigned i = 1; i <= 16; ++i)g[i].reset();
		for (unsigned i = 1; i <= n; ++i) { fgets(M[i] + 1, 6, stdin); }
		c = 1;
		for (unsigned i = 1; i <= n; ++i) {
			for (unsigned j = 1; j <= n;) {
				if (M[i][j] == '.') { m1[i][j] = c; ++j; }
				else {
					if (j != 1)++c;
					while (M[i][j] != '.')++j;
				}
			}
			if (M[i][n] == '.')++c;
		}
		c = 1;
		for (unsigned i = 1; i <= n; ++i) {
			for (unsigned j = 1; j <= n;) {
				if (M[j][i] == '.') { m2[j][i] = c; ++j; }
				else {
					if (j != 1)++c;
					while (M[j][i] != '.')++j;
				}
			}
			if (M[n][i] == '.')++c;
		}
		for (unsigned i = 1; i <= n; ++i) {
			for (unsigned j = 1; j <= n; ++j) {
				if (m1[i][j] != 0 && m2[i][j] != 0) {
					g[m1[i][j]][m2[i][j] + off] = g[m2[i][j] + off][m1[i][j]] = 1;
					//printf("%u %u\n", m1[i][j], m2[i][j] + off);
				}
			}
		}
		//test();
		for (unsigned i = 1; i <= 8; ++i) {
			vis.reset(); if (dfs(i))++a;
		}
		printf("%u\n", a);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章