中国象棋

小葱同学今天要出门下棋,而小葱最喜欢的就是中国象棋里面的将。现在小葱制造了一个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, Mleq 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 * m / 2);
    else printf("%d
", 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
", ans);
  return 0;
}
原文地址:https://www.cnblogs.com/kylinbalck/p/11823179.html