zoukankan      html  css  js  c++  java
  • [JSOI2009]游戏 二分图博弈

    题面

    题面

    题解

    二分图博弈的模板题,只要会二分图博弈就可以做了,可以当做板子打。
    根据二分图博弈,如果一个点x在某种方案中不属于最大匹配,那么这是一个先手必败点。
    因为对方先手,因此我们就是要找这样一个点。
    观察点x的性质,对于这样一个点x,我们一定可以找到一个点来代替它的位置,而什么样的点可以代替它呢?
    从x出发,能够到达的未匹配同侧点可以,只需要交换匹配边即可。
    因此做几遍dfs就可以了

    #include<bits/stdc++.h>
    using namespace std;
    #define R register int
    #define AC 110
    #define ac 81000//向四周连边
    
    int n, m, num;
    int Head[ac], Next[ac], date[ac], tot;
    int link[ac], match[ac], id[AC][AC], a[6] = {-1, 1, 0, 0}, b[6] = {0, 0, -1, 1};
    char s[AC][AC];
    bool vis[ac], z[ac];
    
    struct node{int x, y;}back[ac];
    
    inline void add(int f, int w)
    {
        date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot;
        date[++ tot] = f, Next[tot] = Head[w], Head[w] = tot;
    }
    
    bool dfs(int x)
    {
        for(R i = Head[x]; i; i = Next[i])
        {
            int now = date[i];
            if(vis[now]) continue;
            vis[now] = true;
            if(!link[now] || dfs(link[now])) 
            {
                link[now] = x, match[x] = now; 
                return 1;
            }
        }
        return 0;
    }
    
    void cal()
    {
        memset(link, 0, sizeof(link));
        for(R i = 1; i <= num; i ++)
        {
            if(!(i & 1)) continue;//每次都是从S集合出发的
            memset(vis, 0, sizeof(vis)), dfs(i);//不需要知道最大匹配的大小,只需要知道某一种合法方案即可
        }	
    }
    
    void pre()
    {
        scanf("%d%d", &n, &m), num = n * m;
        for(R i = 1; i <= n; i ++) scanf("%s", s[i] + 1);
        int tmp1 = 1, tmp2 = 2;
        for(R i = 1; i <= n; i ++)
        {
            for(R j = 1; j <= m; j ++)
            {
                if((i + j) & 1) id[i][j] = tmp2, back[tmp2] = (node){i, j}, tmp2 += 2;
                else id[i][j] = tmp1, back[tmp1] = (node){i, j}, tmp1 += 2;
            }
        }
        
        for(R i = 1; i <= n; i ++)
            for(R j = 1; j <= m; j ++)
            {
                if(s[i][j] == '#') continue;
                for(R k = 0; k <= 3; k ++)
                {
                    int x = i + a[k], y = j + b[k];
                    if(x <= 0 || x > n || y <= 0 || y > m || s[x][y] == '#') continue;
                    int s = id[i][j], t = id[x][y];
                    (s & 1) ? add(s, t) : add(t, s);//由编号为奇数的点向编号为偶数的点连边
                }
            }
    }
    
    void dfs1(int x)
    {
        if(z[x]) return ;
        z[x] = true;
        for(R i = Head[x]; i; i = Next[i]) dfs1(link[date[i]]);//这样才能保证dfs到的是同侧的点,
    }//只有同侧的点才能保证经过的匹配边和非匹配边数量相同,可以互相交换
    
    void dfs2(int x)
    {
        if(z[x]) return ;
        z[x] = true;
        for(R i = Head[x]; i; i = Next[i]) dfs2(match[date[i]]);
    }
    
    void work()
    {
        for(R i = 1; i <= num; i ++) 
        {
            if(s[back[i].x][back[i].y] == '#') continue;
            if((i & 1) && !match[i]) dfs1(i);
            else if(!(i & 1) && !link[i]) dfs2(i);//注意要从同侧的非匹配点dfs到同侧的匹配点,这样才能互换方案
        }
        bool done = false;
        for(R i = 1; i <= n; i ++)
            for(R j = 1; j <= m; j ++)
                if(z[id[i][j]]) {done = true; break;}
        if(!done) {printf("LOSE
    "); return ;}
        printf("WIN
    ");
        for(R i = 1; i <= n; i ++) 
            for(R j = 1; j <= m; j ++)
                if(z[id[i][j]]) printf("%d %d
    ", i, j);
            
    /*	for(R i = 1; i <= num; i ++) if(z[i]) {done = true; break;}
        if(!done) {printf("LOSE
    "); return ;}
        printf("WIN
    ");
        for(R i = 1; i <= num; i ++) 
            if(z[i]) printf("%d %d
    ", back[i].x, back[i].y);*/
    }
    
    int main()
    {
    //	freopen("in.in", "r", stdin);
        pre();
        cal();
        work();
    //	fclose(stdin);
        return 0;
    }
    
  • 相关阅读:
    lamp
    mysql多实例部署
    mysql进阶
    rsync
    mysql基础
    httpd
    ftp
    高级命令之awk
    NFS
    网络进阶管理
  • 原文地址:https://www.cnblogs.com/ww3113306/p/10341878.html
Copyright © 2011-2022 走看看