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

1443: [JSOI2009]游戏Game

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 1245   Solved: 575
[ Submit][ Status][ Discuss]

Description

Input

输入数据首先输入两个整数N,M,表示了迷宫的边长。 接下来N行,每行M个字符,描述了迷宫。

Output

若小AA能够赢得游戏,则输出一行"WIN",然后输出所有可以赢得游戏的起始位置,按行优先顺序输出 每行一个,否则输出一行"LOSE"(不包含引号)。

Sample Input

3 3
.##
...
#.#

Sample Output

WIN
2 3
3 2

HINT

对于100%的数据,有1≤n,m≤100。 对于30%的数据,有1≤n,m≤5。

Source

[ Submit][ Status][ Discuss]

第一道二分图博弈
感觉比想象的要简单
首先你一步我一步就把这个图搞成了一个二分图
我们来分析一下
加入我们能够找到这个图的完美匹配,那么当AA放下棋子之后,YY无论怎么走都要经过匹配边,接着AA走的一定是非匹配边,当走完最后一条匹配边是,AA就输了,所以如果有完美匹配直接输出LOSE
反之,如果没有完美匹配,那么一定有非匹配点
AA在非匹配点放下棋子是唯一选择,因为前面分析过放在匹配点一定输
接下来YY一定会走一条非匹配边到匹配点,因为如果还有非匹配点的话当前点一定不是非匹配点
到了匹配点AA就可以走匹配边了,而且一直走到他赢为止,为什么呢?假设当前匹配点可以到达一个非匹配点,那么一定可以再次增广,这就不满足匹配算法结束条件,不存在的。
所以说我们要求出所有可能是非匹配点的点
这就有点技巧了,考虑我们的匹配点,如果他可以到非匹配点,那么它将当前匹配边转向指向那个非匹配点是对答案没有影响的,而当前匹配点就变成了非匹配点,所以它就成为了一个答案。
由此得出了算法

枚举所有的非匹配点,从这个点开始,找一条到达匹配点的非匹配边,这个匹配点有一个匹配v,现在假设我们将当前点与匹配点匹配,那么v点成了非匹配点,于是乎就再dfs v点重复同样的步骤,即可找出所有的非必定匹配点

#include <bits/stdc++.h>
#define mp make_pair
using namespace std;
 
typedef pair< int, int > pp;
int n, m, top, ord[105][105], head[10005], tot;
vector< int > pe;
pp arc[10005];
 
struct Node
{
    int y, nxt;
    Node() {    }
    Node( int y, int nxt ) : y(y), nxt(nxt) {   }
} e[100005];
 
void Adde( int x, int y )
{
    e[++top] = Node(y, head[x]), head[x] = top;
    e[++top] = Node(x, head[y]), head[y] = top;
}
 
char s[105][105];
 
bool vis[10005], ans[10005];
int mat[10005];
 
bool Find( int u )
{
    for(int i = head[u]; i; i = e[i].nxt)
    {
        int v = e[i].y;
        if(vis[v]) continue;
        vis[v] = 1;
        if(!mat[v] || Find(mat[v])) return (mat[u] = v, mat[v] = u);
    }
    return false;
}
 
void Hung()
{
    for(int i = 0; i < (int)pe.size(); ++i)
    {
        if(mat[pe[i]]) continue;
        memset(vis, 0, sizeof(vis));
        tot += Find(pe[i]);
    }
}
 
void Dfs( int u )
{
    ans[u] = 1;
    for(int i = head[u]; i; i = e[i].nxt)
    {
        int v = e[i].y;
        if(mat[v] && !ans[mat[v]]) Dfs(mat[v]);
    }
}
 
int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n ; ++i)
        scanf( "%s", s[i] + 1 );
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j)
        {
            ord[i][j] = (i - 1) * m + j;
            arc[ord[i][j]] = mp(i, j);
            if(s[i][j] == '.')
            {
                if(s[i + 1][j] == '.') Adde(ord[i][j], ord[i][j] + m);
                if(s[i][j + 1] == '.') Adde(ord[i][j], ord[i][j] + 1);
                if(s[i - 1][j] == '.') Adde(ord[i][j], ord[i][j] - m);
                if(s[i][j - 1] == '.') Adde(ord[i][j], ord[i][j] - 1);
                pe.push_back(ord[i][j]);
            }
        }
    Hung();
    if(tot * 2 == pe.size())
    {
        puts("LOSE");
        return 0;
    }
    puts("WIN");
    for(int i = 0; i < (int)pe.size(); ++i)
        if(!mat[pe[i]]) Dfs(pe[i]);
    for(int i = 0; i < (int)pe.size(); ++i)
        if(ans[pe[i]]) printf( "%d %d\n", arc[pe[i]].first, arc[pe[i]].second );
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值