一、題目描述
樣例輸入:
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);
}
}