【题解】JSOI2009游戏

真的没想到、、、果然反应太迟钝,看到题目毫无思路,一点联想都没有。

按照网上博客的说法:一眼棋盘染色二分->二分图->最大匹配->BINGO?果然我还是太弱了……

我们将棋盘黑白染色,相邻两点之间的转移转化为图上的边。根据最大匹配的定义,如果我们最开始将棋子放在一个未匹配的点上,一定会到达一个匹配点(不然若到达了另一个未匹配点就说明还有可以扩张的匹配。)此后,因为已经是最大匹配所以不存在可以增广的交替路。所以这样先手一定会比后手多走一步,获得胜利。所以问题转化为:有哪些点不在最大匹配上?注意最大匹配的情况数是很多的。

所以我们枚举每一个点是否可以不被纳入最大匹配,若可以,说明是一个先手必胜点。

#include <bits/stdc++.h>
using namespace std;
#define maxn 205
int n, m, a[maxn][maxn], link[maxn * 100];
int Map[maxn * 100][5], ans[maxn * 100][2];
int cnt, tot, id[maxn][maxn];
int px[4] = { 0, 1, -1, 0 }, py[4] = { 1, 0, 0, -1 };
bool vis[maxn * 100];
char s[maxn];

int read()
{
    int x = 0, k = 1;
    char c;
    c = getchar();
    while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * k;
}

bool dfs(int u)
{
    for(int i = 1; i <= Map[u][0]; i ++)
    {
        int v = Map[u][i];
        if(vis[v]) continue; vis[v] = 1;
        if(!link[v] || dfs(link[v])) 
            { link[v] = u, link[u] = v; return 1; }
    }
    return 0;
}

void add(int u, int v) { Map[u][++ Map[u][0]] = v; }

int main()
{
    n = read(), m = read();
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
            id[i][j] = ++ cnt;
    for(int i = 1; i <= n; i ++)
    {
        scanf("%s", s + 1);
        for(int j = 1; j <= n; j ++)
            if(s[j] == '#') a[i][j] = 1;
    }
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
            if(!a[i][j] && (i + j) % 2)
                for(int k = 0; k <= 3; k ++)
                {
                    int mx = i + px[k], my = j + py[k];
                    if(mx >= 1 && mx <= n && my >= 1 && my <= m && !a[mx][my])
                    {
                        add(id[i][j], id[mx][my]);
                        add(id[mx][my], id[i][j]);
                    }
                }
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
            if(!a[i][j] && (i + j) % 2)
            {
                memset(vis, 0, sizeof(vis));
                if(dfs(id[i][j])) cnt ++;
            }
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
            if(!a[i][j])
            {
                memset(vis, 0, sizeof(vis)); vis[id[i][j]] = 1;
                if(!link[id[i][j]] || dfs(link[id[i][j]]))
                {
                    ans[++ tot][0] = i, ans[tot][1] = j;
                    link[id[i][j]] = 0;
                }
            }
    if(!tot) printf("LOSE");
    else 
    {
        printf("WIN\n");
        for(int i = 1; i <= tot; i ++)
            printf("%d %d\n", ans[i][0], ans[i][1]);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/twilight-sx/p/8784756.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值