前置知識:棋盤走路博弈問題
例:LOJ#6033. 「雅禮集訓 2017 Day2」棋盤遊戲
問題描述:
棋盤上有一個棋子,兩個人輪流走,每次可以往相鄰四個方向走,不能碰到障礙或已經走過的點,不能走的人輸,問先手是否必勝。
分析:
首先將棋盤黑白染色形成二分圖,然後將可以互相到達的格子連邊。
如果這個二分圖有完美匹配,那麼先手走匹配邊,後手只能走非匹配邊,先手必勝。
如果沒有完美匹配,那麼看起點是否一定在最大匹配中(二分圖最大匹配關鍵點):
如果起點一定在最大匹配中,那麼先手走匹配邊,後手只能走非匹配邊,如此交替。如果後手走到了一個未匹配的點,那麼說明找到了一條交錯路,這與起點一定在最大匹配中矛盾,所以後手走到的點一定是匹配點,此時先手必定可以繼續,所以先手必勝。
如果起點不一定在最大匹配中,即存在一個最大匹配使得起點不是匹配點,那麼先手走一條非匹配邊,後手走一條匹配邊,如此交替。如果先手走到了一個未匹配點,說明找到了增廣路,與最大矛盾,所以先手走到的點一定是匹配點,此時後手必定可以繼續,所以後手必勝。
如何判斷一個點是否在最大匹配中:
回到HDU3446 daizhenyang’s chess
題目描述:
給出棋盤上每個點的可達點,一個棋子,兩人輪流走,不能走走過的,不能走的人輸,問先手是否必勝。
題目分析:
問題相當於是把二分圖變成了一般圖,上面對於是否必勝的分析仍然是適用的,即 起點是否一定在最大匹配中,但是一般圖無法像二分圖一樣求關鍵點,但是這個題只有一個起點,所以我們先求出全圖的最大匹配,然後刪去起點,再求出最大匹配與原來的比較即可。
或者可以先求出刪去起點的最大匹配,然後加入起點,看是否存在增廣路。
Code(注意斜着走不是無限的,只能走它畫出來那麼遠):
#include<bits/stdc++.h>
#define maxn 255
using namespace std;
int cnt,mat[maxn],pre[maxn],typ[maxn],f[maxn],vis[maxn],tim,q[maxn],l,r;
vector<int>G[maxn];
int find(int x){return !f[x]?x:f[x]=find(f[x]);}
int LCA(int u,int v){
for(++tim;;swap(u,v)) if(u){
if(vis[u=find(u)]==tim) return u;
vis[u]=tim,u=pre[mat[u]];
}
}
void blossom(int u,int v,int tp){
for(;find(u)!=tp;u=pre[v]){
pre[u]=v,v=mat[u];
if(typ[v]==2) typ[q[++r]=v]=1;
f[u]=f[v]=tp;
}
}
bool aug(int s){
for(int i=1;i<=cnt;i++) typ[i]=pre[i]=f[i]=0; typ[q[l=r=1]=s]=1;
while(l<=r){
int u=q[l++];
for(int i=G[u].size()-1,v;i>=0;i--) if(typ[v=G[u][i]]!=2&&find(u)!=find(v)){
if(!typ[v]){
pre[v]=u,typ[v]=2;
if(mat[v]) typ[q[++r]=mat[v]]=1;
else {while(v) u=mat[pre[v]],mat[mat[v]=pre[v]]=v,v=u; return 1;}
}
else {int lca=LCA(u,v); blossom(u,v,lca),blossom(v,u,lca);}
}
}
return 0;
}
int n,m,X,id[17][17];
char a[17][17];
int dx[12]={1,-1,0,0,-1,-1,1,1,-2,-2,2,2};
int dy[12]={0,0,1,-1,2,-2,2,-2,1,-1,1,-1};
void add(int p,int x,int y){
if(x<1||x>n||y<1||y>m||a[x][y]!='.') return;
G[p].push_back(id[x][y]);
}
int main()
{
int T; scanf("%d",&T);
for(int t=1;t<=T;t++){
scanf("%d%d",&n,&m),cnt=0;
for(int i=1;i<=n;i++){
scanf("%s",a[i]+1);
for(int j=1;j<=m;j++){
if(a[i][j]!='#') id[i][j]=++cnt;
if(a[i][j]=='K') X=cnt;
}
}
for(int i=1;i<=cnt;i++) mat[i]=0,G[i].clear();
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(a[i][j]!='#'){
int p=id[i][j];
for(int k=1;k<=2;k++) if(k)
add(p,i+k,j+k),add(p,i+k,j-k),add(p,i-k,j+k),add(p,i-k,j-k);
for(int k=0;k<12;k++) add(p,i+dx[k],j+dy[k]);
}
for(int i=1;i<=cnt;i++) if(i!=X&&!mat[i]) aug(i);
printf("Case #%d: daizhenyang %s\n",t,aug(X)?"win":"lose");
}
}