bzoj 2437 [Noi2011]兔兔与蛋蛋 二分图博弈

题面

题目传送门

解法

不妨把问题转化成空格的移动,将一开始空格的位置变成黑色

那么,真实颜色和后来染的颜色不同的就一定不会经过

然后就变成了一个二分图博弈问题了

考虑一下什么时候先手必胜:

就是起点一定在二分图的最大匹配上的时候

否则后手必胜

先手走错的情况满足在他走之前先手必胜,走之后先手还是必胜

匈牙利算法即可

代码

#include <bits/stdc++.h>
#define N 50
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Edge {
    int next, num;
} e[N * N * N * N];
int dx[5] = {0, -1, 1, 0, 0}, dy[5] = {0, 0, 0, -1, 1};
int n, m, cnt, tot, tim;
int num[N][N], col[N][N], s[N * N], p[N * N], ban[N * N], vis[N * N], ans[N * N];
void add(int x, int y) { 
    e[++cnt] = (Edge) {e[x].next, y};
    e[x].next = cnt;
}
bool dfs(int x) {
    if (ban[x]) return false;
    for (int p = e[x].next; p; p = e[p].next) {
        int k = e[p].num;
        if (vis[k] == tim || ban[k]) continue;
        vis[k] = tim;
        if (!ans[k] || dfs(ans[k])) {
            ans[k] = x, ans[x] = k;
            return true;
        }
    }
    return false;
}
int main() {
    read(n), read(m);
    int x, y;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            char c; cin >> c;
            if (c == '.') x = i, y = j, col[x][y] = 1;
            if (c == 'X') col[i][j] = 1;
        }
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if ((i + j) % 2 != (x + y) % 2 && col[i][j] == 1 || (i + j) % 2 == (x + y) % 2 && !col[i][j])
                col[i][j] = 2;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if (col[i][j] != 2) num[i][j] = ++tot;
    cnt = tot;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            if (col[i][j] == 2) continue;
            for (int k = 1; k <= 4; k++) {
                int tx = i + dx[k], ty = j + dy[k];
                if (tx <= 0 || ty <= 0 || tx > n || ty > m || col[tx][ty] == 2) continue;
                add(num[i][j], num[tx][ty]);
            }
        }
    for (int i = 1; i <= tot; i++)
        if (!ans[i]) tim++, dfs(i);
    int q; read(q);
    for (int i = 1; i <= q * 2; i++) {
        ban[num[x][y]] = 1;
        if (ans[num[x][y]]) {
            int tmp = ans[num[x][y]];
            ans[tmp] = ans[num[x][y]] = 0;
            tim++; p[i] = !dfs(tmp);
        } else p[i] = 0;
        read(x), read(y);
    }
    int ret = 0;
    for (int i = 2; i <= q * 2; i += 2)
        if (p[i] && p[i - 1]) s[++ret] = i / 2;
    cout << ret << "\n";
    for (int i = 1; i <= ret; i++) cout << s[i] << "\n";
    return 0;
}

转载于:https://www.cnblogs.com/copperoxide/p/9476752.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值