1. 程式人生 > >【BZOJ1443】遊戲(二分圖匹配,博弈論)

【BZOJ1443】遊戲(二分圖匹配,博弈論)

() ans evel getchar mes 最大匹配 開始 就會 明顯

【BZOJ1443】遊戲(二分圖匹配,博弈論)

題面

BZOJ

題解

很明顯的二分圖博弈問題。
發現每次移動一定是從一個黑點到達一個白點,或者反過來。
所以可以對於棋盤進行染色然後連邊。
考慮一下必勝策略。
如果選擇從一個匹配點開始走,
另外一個人沿著匹配點走,那麽就輸了,因為匹配點不一定有出邊了。
如果選擇從一個非匹配點開始走,
另外一個人無論怎麽走都只能走到一個匹配點(或者無路可走)
如果另外一個人可以走到一個非匹配點,意味著這兩個點可以匹配,所以不存在這種情況。
那麽,先手只需要沿著匹配邊走就一定能夠做到必勝。
所以黑白染色之後連邊,找到所有不一定在最大匹配中的點就好了。

直接跑匈牙利非常慢啊(雖然能夠過)

匈牙利代碼:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
#define RG register
#define MAX 111
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'
||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=-1,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*t; } struct Line{int v,next;}e[MAX*MAX*4]; int h[MAX*MAX],cnt=1; inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;} char
g[MAX][MAX]; int n,m,bh[MAX][MAX],tot; int match[MAX*MAX],vis[MAX*MAX],tim; int d[4][2]={0,1,1,0,-1,0,0,-1}; bool ban[MAX*MAX]; int ans; pair<int,int> p[MAX*MAX]; bool dfs(int u) { if(ban[u])return false; for(int i=h[u];i;i=e[i].next) if(vis[e[i].v]!=tim&&!ban[e[i].v]) { vis[e[i].v]=tim; if(!match[e[i].v]||dfs(match[e[i].v])) {match[u]=e[i].v;match[e[i].v]=u;return true;} } return false; } int main() { n=read();m=read(); for(int i=1;i<=n;++i)scanf("%s",g[i]+1); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if(g[i][j]!='#')bh[i][j]=++tot; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if(((i+j)&1)&&g[i][j]=='.') for(int k=0;k<4;++k) { int x=i+d[k][0],y=j+d[k][1]; if(x<1||x>n||y<1||y>m||g[x][y]=='#')continue; Add(bh[i][j],bh[x][y]);Add(bh[x][y],bh[i][j]); } for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if(((i+j)&1)&&g[i][j]=='.') if(!match[bh[i][j]])++tim,dfs(bh[i][j]); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if(g[i][j]=='.') { if(!match[bh[i][j]]){p[++ans]=make_pair(i,j);continue;} ban[bh[i][j]]=true;int nw=match[bh[i][j]]; match[bh[i][j]]=match[nw]=0;++tim; if(dfs(nw))p[++ans]=make_pair(i,j); else match[bh[i][j]]=nw,match[nw]=bh[i][j]; ban[bh[i][j]]=false; } puts(ans?"WIN":"LOSE");sort(&p[1],&p[ans+1]); for(int i=1;i<=ans;++i)printf("%d %d\n",p[i].first,p[i].second); return 0; }

跑網絡流就會快很多。
但是怎麽判斷一個點是否在不一定在最大匹配中呢?
把所有和\(S\)相連的點(用還有剩余流量的邊連接)全部扣下來,這些點一定滿足條件。
為什麽呢?
首先不在當前的這個匹配中的點一定會被計算。
如果一個點在匹配中,但是他被連上了,那麽一定是通過一個沒有被匹配上的點,到達當前點的匹配點,在連回來的,這樣子意味著可以交換匹配。
\(T\)相連的點同理解決即可。
這樣子快很多。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 111
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
struct Line{int v,next,w;}e[MAX*MAX*10];
int h[MAX*MAX],cnt=2;
inline void Add(int u,int v,int w)
{
    e[cnt]=(Line){v,h[u],w};h[u]=cnt++;
    e[cnt]=(Line){u,h[v],0};h[v]=cnt++;
}
char g[MAX][MAX];
int n,m,bh[MAX][MAX],tot,cur[MAX*MAX];
bool vis[MAX*MAX],cho[MAX*MAX];
int d[4][2]={0,1,1,0,-1,0,0,-1};
int ans,col[MAX*MAX];
pair<int,int> p[MAX*MAX];
int level[MAX*MAX],S,T;
bool bfs()
{
    queue<int> Q;memset(level,0,sizeof(level));
    level[S]=1;Q.push(S);
    while(!Q.empty())
    {
        int u=Q.front();Q.pop();
        for(int i=h[u];i;i=e[i].next)
            if(e[i].w&&!level[e[i].v])
            {
                level[e[i].v]=level[u]+1,Q.push(e[i].v);
                if(e[i].v==T)break;
            }
    }
    return level[T];
}
int dfs(int u,int flow)
{
    if(u==T||!flow)return flow;
    int ret=0;
    for(int &i=cur[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(e[i].w&&level[v]==level[u]+1)
        {
            int d=dfs(v,min(flow,e[i].w));
            ret+=d;flow-=d;
            e[i].w-=d;e[i^1].w+=d;
            if(!flow)break;
        }
    }
    return ret;
}
void DFS(int u,int d)
{
    vis[u]=true;
    if(col[u]==d)++ans,cho[u]=true;
    for(int i=h[u];i;i=e[i].next)
        if(e[i].w==d&&!vis[e[i].v])DFS(e[i].v,d);
}
void Dinic()
{
    int ret=0;
    while(bfs())
    {
        for(int i=S;i<=T;++i)cur[i]=h[i];
        ret+=dfs(S,1e9);
    }
    return;
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)scanf("%s",g[i]+1);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            if(g[i][j]!='#')bh[i][j]=++tot;
    S=0;T=tot+1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            if(((i+j)&1)&&g[i][j]=='.')
                for(int k=0;k<4;++k)
                {
                    int x=i+d[k][0],y=j+d[k][1];
                    if(x<1||x>n||y<1||y>m||g[x][y]=='#')continue;
                    Add(bh[i][j],bh[x][y],1);
                }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            if(g[i][j]=='.')
            {
                if((i+j)&1)Add(S,bh[i][j],1),col[bh[i][j]]=1;
                else Add(bh[i][j],T,1),col[bh[i][j]]=0;
            }
    Dinic();DFS(S,1);DFS(T,0);
    puts(ans?"WIN":"LOSE");sort(&p[1],&p[ans+1]);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            if(bh[i][j]&&cho[bh[i][j]])printf("%d %d\n",i,j);
    return 0;
}

【BZOJ1443】遊戲(二分圖匹配,博弈論)