小蔥同學今天要出門下棋,而小蔥最喜歡的就是中國象棋裏面的將。現在小蔥製造了一個N*M的棋盤。小蔥同學知道最初中國象棋是爲了模擬戰爭而設計出來的,所有爲了更加符合真實情況,小蔥在棋盤上的K個位置設置了小蔥拌豆腐店作爲障礙物。現在小蔥同學邀請大蔥同學一起來下棋,下棋的規則如下:小蔥同學會先在棋盤上找一個非障礙物的位置放上一個將,記該位置爲\(P_1\)。之後,小蔥和大蔥同學會輪流對這個將進行移動,記第i次移動之後的位置爲\(P_{i + 1}\)。在移動過程中,將只能橫着或豎着移動一格,且移動位置中不能碰觸障礙物,同時,每次移動需保證不重複走之前走過的任何位置。誰沒有辦法移動,誰就輸掉比賽。現在小蔥同學想知道,在他們都採用最優策略的情況下,有多少個位置能夠使小蔥同學必勝?
【輸入格式】
第一行三個數:N, M, K
接下來K行每行兩個數x ,y,代表一個障礙物的座標。
【輸出格式】
輸出一行一個數代表答案
【樣例】
3 3 3
1 1
2 2
3 3
【樣例】
2
【數據規模與約定】
對於30%的數據,\(N, M\leq 10\)
對於50%的數據,\(N ,M \leq 20\)
對於70%的數據,\(N, M \leq 50\)
對於另外10%的數據, K = 0
對於100%的數據,\(1 \leq N, M \leq 250,0 \leq K \leq N *M\)
Solution
考慮這是一個棋盤的話,可以黑白染色,於是這就成了一個二分圖,顯然只能有黑點走到白點,白點走到黑點。於是給這兩個點集連邊,求最大匹配。
考慮什麼時候一個點是必勝點?當且僅當它在每一個最大匹配內。
如果說,一個點不在某一個最大匹配內,也就是說,它原來匹配的那個點除了他之外,還有一個能走的點,那麼從這個點出發,走到匹配的點,又從匹配的點走到另一個點,那麼這個點就不是一個必勝點。因爲一個必勝點一定滿足它走到的點是自己的必勝點或者對方的必敗點。那麼這個點的匹配的點在所有的最大匹配裏一定是隻有這一個匹配的。
(似乎對於所有的棋盤博弈論都可以二分圖染色,然後滿足:如果一個點在所有的最大匹配裏,則這個點是必勝點,反之爲必敗點。但我不是很確定……)
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
long long read() {
long long x = 0; int f = 0; char c = getchar();
while (c < '0' || c > '9') f |= c == '-', c = getchar();
while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return f ? -x : x;
}
int n, m, k, map[510][510], mx[4] = {-1, 1, 0, 0}, my[4] = {0, 0, -1, 1};
int dx[510 * 510], id[510][510];
//匹配的對象 每個點的編號
bool v[510 * 510];
struct szh{
int v, nxt;
}a[510 * 510];
int hd[255 * 255 * 4], cnt, bian;
inline void add(int x, int y) {
a[++bian].v = y, a[bian].nxt = hd[x], hd[x] = bian;
}
inline bool dfs(int u) {
for (int i = hd[u], b; b = a[i].v, i; i = a[i].nxt)
if (!v[b]) {
v[b] = 1;
if (!dx[b] || dfs(dx[b])) {//記錄一下對象
dx[b] = u, dx[u] = b; return 1;
}
}
return 0;
}
inline bool _dfs(int u) {
for (int i = hd[u], b; b = a[i].v, i; i = a[i].nxt)
if (!v[b]) {
v[b] = 1;
if (!dx[b] || _dfs(dx[b])) return 1;
}
return 0;
}
int main() {
freopen("cchess.in", "r", stdin);
freopen("cchess.out", "w", stdout);
n = read(); m = read(); k = read();
if (k == 0) {
if ((n + m) & 1) printf("%d\n", n * m / 2);
else printf("%d\n", n * m);
return 0;
}
for (int i = 1, x, y; i <= k; ++i) {
x = read(); y = read();
map[x][y] = 1;
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (!map[i][j]) id[i][j] = ++cnt;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (!map[i][j])
for (int l = 0; l < 4; ++l) {
int x = i + mx[l], y = my[l] + j;
if (map[x][y] || x < 1 || x > n || y < 1 || y > m) continue;
add(id[i][j], id[x][y]);
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if ((i + j) & 1) {//二分圖嘛,只能從一半的點開始走
if (map[i][j]) continue;
memset(v, 0, sizeof v);
dfs(id[i][j]);
}
int ans = 0;
for (int i = 1; i <= cnt; ++i)
if (dx[i]) {
memset(v, 0, sizeof v);
v[i] = 1;
dx[dx[i]] = 0;//把兩個點之間的路砍斷,看他還能不能找到新的對象
//如果能找到就是大豬蹄子,這樣的點當然不能要
if (!_dfs(dx[i])) ans++;
dx[dx[i]] = i;
}
printf("%d\n", ans);
return 0;
}