[BZOJ2437]-[Noi2011]兔兔与蛋蛋-博弈+二分图必匹配点

版权声明:转载嘛....也不是不可以(故作沉思),记得带上me的ID啊qwq https://blog.csdn.net/Izumi_Hanako/article/details/80639246

说在前面

终于把这个史前巨坑填了emmmm


题目

BZOJ24373传送门
看题可进传送门


解法

首先是个网格图
根据我们做 BZOJ1443 以及 BZOJ2463 的经验
我们把相邻的 黑白棋子 连边,然后跑一个最大匹配

(对于兔兔)可以发现,如果空格旁边有一个非匹配白棋子,那么挪动这个棋子之后,蛋蛋只能寻找非匹配边,然后挪动一个黑棋子。由于是最大匹配,这个黑棋子一定是匹配的,所以兔兔只需要按照最大匹配的方案,挪动黑棋子的匹配点即可

那么这个游戏会不会陷入死循环呢?答案是不会的
因为空格的路径不能相交。如果相交,路径长度一定是偶数,那么兔兔和蛋蛋都必须移动 位于交点的棋子,显然矛盾

(对于兔兔)所以,如果空格周围有一个非匹配白点,那么兔兔胜。这个条件等价于,我们把空格看成黑点,并加入匹配,空格是必匹配点(如果空格是非匹配点,那么存在一种最大匹配使得,周围四个点都是必匹配点,此时蛋蛋可以采取类似 1 步骤而获胜)

所以,我们只需要判断,如果 移动前和移动后 的匹配数没有变化,说明兔兔操作错了(没变化相当于,原来的点不是必匹配点)

1 : 蛋蛋只需要选取一种使得周围四个点都有匹配的方案,兔兔每挪动一步,蛋蛋只需要挪动相应匹配点即可。兔兔的挪动是非匹配边,非匹配边是有限的


下面是代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

char mp[45][45] ;
int N , M , K , id[45][45] , id_c , Nx , Ny , head[1605] , tp ;
struct Path{
    int pre , to ;
} p[44*44*4] ;

void In( int t1 , int t2 ){
    if( !t1 || !t2 ) return ;
    p[++tp] = ( Path ){ head[t1] , t2 } ; head[t1] = tp ;
    p[++tp] = ( Path ){ head[t2] , t1 } ; head[t2] = tp ;
}

bool vis[1605] , ban[1605] ;
int match[1605] ;
bool matching( int u ){
    for( int i = head[u] ; i ; i = p[i].pre ){
        int v = p[i].to ;
        if( vis[v] || ban[v] ) continue ;
        vis[v] = true ;
        if( !match[v] || matching( match[v] ) ){
            match[v] = u , match[u] = v ;
            return true ;
        }
    } return false ;
}

void preWork(){
    bool legal_B = ( Nx + Ny ) & 1 ;
    for( int i = 1 ; i <= N ; i ++ )
        for( int j = 1 ; j <= M ; j ++ )
            if( mp[i][j] == 'O' ){
                if( ( ( i + j )&1 ) != legal_B ) id[i][j] = ++id_c ;
            } else if( ( ( i + j )&1 ) == legal_B ) id[i][j] = ++id_c ;
    for( int i = 1 ; i <= N ; i ++ )
        for( int j = 1 ; j <= M ; j ++ ) if( id[i][j] && ( i + j )&1 ){
            In( id[i][j] , id[i+1][j] ) , In( id[i][j] , id[i-1][j] ) ;
            In( id[i][j] , id[i][j+1] ) , In( id[i][j] , id[i][j-1] ) ;
        }
    for( int i = 1 ; i <= id_c ; i ++ ) if( !match[i] ){
        memset( vis + 1 , false , id_c * sizeof( bool ) ) ;
        matching( i ) ;
    }
}

bool inM[2005] ;
void solve(){
    scanf( "%d" , &K ) ;
    int cnt = 0 ;
    for( int i = 1 ; i <= 2 * K ; i ++ ){
        int u = id[Nx][Ny] ;
        ban[u] = true ;
        if( match[u] ){
            int v = match[u] ;
            match[u] = match[v] = 0 ;
            memset( vis + 1 , false , id_c * sizeof( bool ) ) ;
            inM[i] = !matching( v ) ;
        } scanf( "%d%d" , &Nx , &Ny ) ;
        if( i % 2 == 0 && inM[i] && inM[i-1] ) cnt ++ ;
    } printf( "%d\n" , cnt ) ;
    for( int i = 1 ; i <= K ; i ++ )
        if( inM[2*i-1] && inM[2*i] ) printf( "%d\n" , i ) ;
}

int main(){
    scanf( "%d%d" , &N , &M ) ;
    for( int i = 1 ; i <= N ; i ++ ){
        scanf( "%s" , mp[i] + 1 ) ;
        for( int j = 1 ; j <= M ; j ++ )
            if( mp[i][j] == '.' ) Nx = i , Ny = j ;
    } preWork() ; solve() ;
}

猜你喜欢

转载自blog.csdn.net/Izumi_Hanako/article/details/80639246
今日推荐