bzoj 1443 [JSOI2009]游戏Game 二分图博弈

题面

题目传送门

解法

显然这是一个二分图

若二分图求完最大匹配之后不存在非匹配点,那么一定不存在必胜策略

因为我们求完最大匹配之后,一定不存在增广路,只存在交错路,对方从匹配点出发,最后一定可以找到一条匹配边作为结束,所以先手必胜

那么,从一个非匹配点出发,一定是一个后手必胜的策略,但是非匹配点并不是全部答案,因为并不是只存在一组最大匹配,所以从每个非匹配点出发,寻找交错路,然后访问到的和非匹配点处于同一集合的点都是答案

匈牙利算法即可

代码

#include <bits/stdc++.h>
#define N 110
using namespace std;
struct Edge {
    int next, num;
} e[N * N * 16];
int dx[5] = {0, -1, 1, 0, 0}, dy[5] = {0, 0, 0, -1, 1};
int cnt, num[N][N], l[N * N], tx[N * N], fl[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) {
    for (int p = e[x].next; p; p = e[p].next) {
        int k = e[p].num;
        if (!vis[k]) {
            vis[k] = 1;
            if (!ans[k] || dfs(ans[k])) {
                ans[k] = x, tx[x] = k;
                return true;
            }
        }
    }
    return false;
}
void work(int x) {
    if (fl[x]) return;
    fl[x] = 1;
    for (int p = e[x].next; p; p = e[p].next) {
        int k = e[p].num;
        if (l[k] && tx[k]) work(tx[k]);
        if (!l[k] && ans[k]) work(ans[k]);
    }
}
int main() {
    int n, m, tot = 0; cin >> n >> m;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            char c; cin >> c;
            if (c != '#') num[i][j] = ++tot, l[tot] = (i + j) % 2;
        }
    cnt = tot;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            if (!num[i][j]) 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 || !num[tx][ty]) continue;
                add(num[i][j], num[tx][ty]);
            }
        }
    int sum = 0;
    memset(ans, 0, sizeof(ans));
    for (int i = 1; i <= tot; i++) {
        if (!l[i]) continue;
        memset(vis, 0, sizeof(vis));
        if (dfs(i)) sum++;
    }
    if (2 * sum == tot) {cout << "LOSE\n"; return 0;}
    cout << "WIN\n";
    memset(fl, 0, sizeof(fl));
    for (int i = 1; i <= tot; i++)
        if ((l[i] && !tx[i]) || (!l[i] && !ans[i]))
            work(i);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if (fl[num[i][j]]) cout << i << ' ' << j << "\n";
    return 0;
}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值