BZOJ1443-[JSOI2009]游戏Game-博弈+二分图必匹配点

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

写在前面

并没有什么想说的,但是要保持格式=w=


题目

BZOJ1443传送门
看题可戳传送门


解法

其实和 BZOJ2463 思路差不多
在棋盘上走,不染个色就亏了对吧 =w=

(思路来着Menci)
那么我们黑白染色一下,然后跑有效格子的最大匹配
假设我们一开始放在一个非匹配点上,那么先手要么就不能移动,要么移动到一个匹配点上,而后手可以沿着匹配边移动,这就是一个寻找增广路的过程,因为是最大匹配,所以最后一定停在一开始的一侧。所以我们可以知道,所有的非匹配点,如果作为起点,就是先手必败的

如果一开始放在最大匹配上呢?先手沿匹配边走,后手沿非匹配边走。如果后手走到一个非匹配点,先手就输了,而我们将这条路径取反,就是另一个最大匹配。所以不一定在最大匹配中的点,作为起点就是先手必败的

然后求不一定在最大匹配中的点,跑一遍Dinic之后,dfs打标记就好了

一些自己的思考:
在这个题里面的先手必胜和必败,都是对于 确定的局面 而言的
局面才是状态,而棋子位置不是


下面是代码

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

bool inS[105*105] , Ppos[105*105] , vis[105*105] ;
char mp[105][105] ;
int N , M , tp = 1 , id[105][105] , id_c , head[105*105] , S , T , Ppos_cnt ;
struct Path{
    int pre , to , flow ;
} p[105*105*6] ;

void In( int t1 , int t2 ){
    p[++tp] = ( Path ){ head[t1] , t2 , 1 } ; head[t1] = tp ;
    p[++tp] = ( Path ){ head[t2] , t1 , 0 } ; head[t2] = tp ;
}

void preWork(){
    for( int i = 1 ; i <= N ; i ++ )
    for( int j = 1 ; j <= M ; j ++ ) if( id[i][j] && ( ( i + j )&1 ) ){
        if( id[i+1][j] ) In( id[i][j] , id[i+1][j] ) ;
        if( id[i-1][j] ) In( id[i][j] , id[i-1][j] ) ;
        if( id[i][j+1] ) In( id[i][j] , id[i][j+1] ) ;
        if( id[i][j-1] ) In( id[i][j] , id[i][j-1] ) ;
        In( S , id[i][j] ) , inS[ id[i][j] ] = true ;
    } else if( id[i][j] ) In( id[i][j] , T ) ;
}

int dis[105*105] , que[105*105] , fr , ba ;
bool BFS(){
    memset( dis , -1 , sizeof( dis ) ) ;
    fr = 1 , que[ ba = 1 ] = S , dis[S] = 0 ;
    while( ba >= fr ){
        int u = que[fr++] ;
        for( int i = head[u] ; i ; i = p[i].pre ){
            int v = p[i].to ;
            if( dis[v] != -1 || !p[i].flow ) continue ;
            dis[v] = dis[u] + 1 , que[++ba] = v ;
        }
    } return dis[T] != -1 ;
}

int dfs_dinic( int u , int flow ){
    if( u == T ) return flow ;
    int rt = 0 ;
    for( int i = head[u] ; i ; i = p[i].pre ){
        int v = p[i].to , nowf ;
        if( dis[v] != dis[u] + 1 || !p[i].flow ) continue ;
        if( ( nowf = dfs_dinic( v , min( flow , p[i].flow ) ) ) ){
            p[i].flow -= nowf ;
            p[i^1].flow += nowf ;
            flow -= nowf , rt += nowf ;
            if( !flow ) break ;
        }
    } if( flow ) dis[u] = -1 ;
    return rt ;
}

void dfs_solve( int u , int ef , bool sid ){
    vis[u] = true ;
    if( inS[u] == sid ) Ppos[u] = true , Ppos_cnt ++ ;
    for( int i = head[u] ; i ; i = p[i].pre ){
        int v = p[i].to ;
        if( p[i].flow == ef && !vis[v] ) dfs_solve( v , ef , sid ) ;
    }
}

void solve(){
    while( BFS() ) dfs_dinic( S , 0x3f3f3f3f ) ;

    dfs_solve( S , 1 , true ) ;
    memset( vis , false , sizeof( vis ) ) ;
    dfs_solve( T , 0 , 0 ) ;

    puts( Ppos_cnt ? "WIN" : "LOSE" ) ;
    for( int i = 1 ; i <= N ; i ++ )
        for( int j = 1 ; j <= M ; j ++ )
            if( Ppos[ id[i][j] ] ) printf( "%d %d\n" , i , j ) ;
}

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] != '#' ) id[i][j] = ++id_c ;
    } S = ++id_c , T = ++id_c ; 
    preWork() ; solve() ;
}

猜你喜欢

转载自blog.csdn.net/Izumi_Hanako/article/details/80626293