LOJ6033「雅礼集训 2017 Day2」棋盘游戏 (博弈论,二分图,匈牙利算法)

什么神仙思路啊……

看到棋盘就去想二分图。(smg啊)(其实是校内模拟赛有基本一样的题,只不过直接给了个二分图)

看到二分图就去想最大匹配。(我怎么想偶环的性质去了)

(以下内容摘自这里

这个二分图的某种最大匹配方案中,从非匹配点出发先手必败:先手只能走到匹配点(否则不是最大匹配),后手只需要一直走匹配边即可,先手操作时不可能走到非匹配点(否则存在增广路,与最大匹配矛盾),所以先手必败。

容易发现,当且仅当出发点一定在最大匹配中,先手才会胜利。

(注:这里我觉得有点问题,虽然我大概能感受到究竟为什么是对的,但我说不出,所以咕了)

所以,一个起点使先手必胜当且仅当它一定在最大匹配中。

判断的话,先求出任意一个最大匹配。然后对有匹配的点,如果与他有匹配的点能找到另一个匹配,那么它实际上不合法,否则合法。

用匈牙利算法即可做到 \(O((nm)^2)\)。实际上常数小,跑不满,跑得飞快。


代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=100010,mod=998244353,d[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
int n,m,el,cnt,head[maxn],to[maxn],nxt[maxn],dis[maxn],with[maxn],x[maxn],y[maxn],id[111][111],tot;
char mp[111][111];
bool vis[maxn],ans[maxn];
inline void add(int u,int v){
    to[++el]=v;nxt[el]=head[u];head[u]=el;
}
void dfs(int u){
    for(int i=head[u];i;i=nxt[i]){
        int v=to[i];
        if(~dis[v]) continue;
        dis[v]=dis[u]^1;
        dfs(v);
    }
}
bool dfs2(int u){
    if(vis[u]) return false;
    vis[u]=true;
    for(int i=head[u];i;i=nxt[i]){
        int v=to[i];
        if(!with[v] || dfs2(with[v])){
            with[with[u]]=0;
            with[u]=v;with[v]=u;
            return true;
        }
    }
    return false;
}
int main(){
    n=read();m=read();
    FOR(i,1,n) scanf("%s",mp[i]+1);
    FOR(i,1,n) FOR(j,1,m) if(mp[i][j]=='.') id[i][j]=++cnt,x[cnt]=i,y[cnt]=j;
    FOR(i,1,n) FOR(j,1,m) if(mp[i][j]=='.'){
        FOR(k,0,3){
            int ti=i+d[k][0],tj=j+d[k][1];
            if(ti<1 || ti>n || tj<1 || tj>m || mp[ti][tj]=='#') continue;
            add(id[i][j],id[ti][tj]);
        }
    }
    MEM(dis,-1);
    FOR(i,1,cnt) if(dis[i]==-1) dis[i]=0,dfs(i);
    FOR(i,1,cnt) if(dis[i]){
        MEM(vis,0);
        dfs2(i);
    }
    FOR(i,1,cnt) if(with[i]){
        MEM(vis,0);
        ans[i]=!dfs2(with[i]);
    }
    FOR(i,1,cnt) if(!ans[i]) tot++;
    printf("%d\n",tot);
    FOR(i,1,cnt) if(!ans[i]) printf("%d %d\n",x[i],y[i]);
}

猜你喜欢

转载自www.cnblogs.com/1000Suns/p/11806024.html