【问题描述】
从前有一个王国,这个王国的城堡是一个矩形,被分为M×N个方格。一些方格是墙,而另一些是空地。这个王国的国王在城堡里设了一些陷阱,每个陷阱占据一块空地。
一天,国王决定在城堡里布置守卫,他希望安排尽量多的守卫。守卫们都是经过严格训练的,所以一旦他们发现同行或同列中有人的话,他们立即向那人射击。因此,国王希望能够合理地布置守卫,使他们互相之间不能看见,这样他们就不可能互相射击了。守卫们只能被布置在空地上,不能被布置在陷阱或墙上,且一块空地只能布置一个守卫。如果两个守卫在同一行或同一列,并且他们之间没有墙的话,他们就能互相看见。(守卫就像象棋里的车一样)
你的任务是写一个程序,根据给定的城堡,计算最多可布置多少个守卫,并设计出布置的方案。
【输入】
第一行两个整数M和N(1≤M,N≤200),表示城堡的规模。
接下来M行N列的整数,描述的是城堡的地形。第i行j列的数用ai,j表示。
ai,j=0,表示方格[i,j]是一块空地;
ai,j=1,表示方格[i,j]是一个陷阱;
ai,j=2,表示方格[i,j]是墙。
【输出】
第一行一个整数K,表示最多可布置K个守卫。
此后K行,每行两个整数xi和yi,描述一个守卫的位置。
【样例】
guards.in
3 4
2 0 0 0
2 2 2 1
0 1 0 2
guards.out
2
1 2
3 3
样例数据如图(黑色方格为墙,白色方格为空地,圆圈为陷阱,G表示守卫)
這是一道二分圖最大匹配。
首先建模,將被牆隔開的各個橫塊,作為二分圖的左半集;
再將被牆隔開的各個豎塊,作為二分圖的右半集;
若一個橫塊跟一個豎塊的交點為空地,則將它們連一條邊。
然後求一個最大匹配,輸出所有匹配就行了。
Accode:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <bitset>
using std::bitset;
const char fi[] = "guards.in";
const char fo[] = "guards.out";
const int maxN = 40010;
const int maxR = 210;
const int MAX = 0x3fffff00;
const int MIN = -MAX;
struct Edge {int dest; Edge *next; };
Edge *edge[maxN];
bitset <maxN> marked;
int hh[maxR][maxR], lx[maxR][maxR];
int map[maxR][maxR];
int Link[maxN];
int x[maxN], y[maxN];
int N, M, n, m, ans;
void init_file()
{
freopen(fi, "r", stdin);
freopen(fo, "w", stdout);
}
inline void insert(int u, int v)
{
Edge *p = new Edge;
p -> dest = v;
p -> next = edge[u];
edge[u] = p;
}
void readdata()
{
scanf("%d%d", &N, &M);
for (int i = 1; i < N + 1; ++i)
for (int j = 1; j < M + 1; ++j)
scanf("%d", &map[i][j]);
for (int i = 1; i < N + 1; ++i)
for (int j = 1; j < M + 1; ++j)
if (map[i][j] != 2)
{
if (!hh[i][j])
{
x[++n] = i; //記錄該橫塊的橫座標。
hh[i][j] = n; //記錄(i, j)所屬的橫塊。
for (int k = j + 1; k < M + 1; ++k)
{
if (map[i][k] == 2) break;
hh[i][k] = n;
} //向右找與此位置相連的區域。
for (int k = j - 1; k; --k)
{
if (map[i][k] == 2) break;
hh[i][k] = n;
} //向左找與此位置相連的區域。
}
if (!lx[i][j])
{
y[++m] = j; //記錄該豎塊的縱座標。
lx[i][j] = m; //記錄(i, j)所屬的豎塊。
for (int k = i + 1; k < N + 1; ++k)
{
if (map[k][j] == 2) break;
lx[k][j] = m;
} //向下找與此位置相連的區域。
for (int k = i - 1; k; --k)
{
if (map[k][j] == 2) break;
lx[k][j] = m;
} //向上找與此位置相連的區域。
}
if (!map[i][j])
insert(hh[i][j], lx[i][j]);
//(i, j)一定要為空地才能連邊。
}
}
bool Find(int u)
{
for (Edge *p = edge[u]; p; p = p -> next)
{
int v = p -> dest;
if (!marked.test(v))
{
marked.set(v);
if (!Link[v] || Find(Link[v]))
{Link[v] = u; return true; }
}
}
return false;
}
void work()
{
for (int i = 1; i < n + 1; ++i)
{marked.reset(); if (Find(i)) ++ans; }
printf("%d\n", ans);
for (int j = 1; j < m + 1; ++j)
if (Link[j])
printf("%d %d\n", x[Link[j]], y[j]);
}
int main()
{
init_file();
readdata();
work();
exit(0);
}