HDU3446 daizhenyang s chess【棋盤走路博弈問題】

前置知識:棋盤走路博弈問題

例:LOJ#6033. 「雅禮集訓 2017 Day2」棋盤遊戲

問題描述:

棋盤上有一個棋子,兩個人輪流走,每次可以往相鄰四個方向走,不能碰到障礙或已經走過的點,不能走的人輸,問先手是否必勝。

分析:

首先將棋盤黑白染色形成二分圖,然後將可以互相到達的格子連邊。

如果這個二分圖有完美匹配,那麼先手走匹配邊,後手只能走非匹配邊,先手必勝。

如果沒有完美匹配,那麼看起點是否一定在最大匹配中(二分圖最大匹配關鍵點):

如果起點一定在最大匹配中,那麼先手走匹配邊,後手只能走非匹配邊,如此交替。如果後手走到了一個未匹配的點,那麼說明找到了一條交錯路,這與起點一定在最大匹配中矛盾,所以後手走到的點一定是匹配點,此時先手必定可以繼續,所以先手必勝。

如果起點不一定在最大匹配中,即存在一個最大匹配使得起點不是匹配點,那麼先手走一條非匹配邊,後手走一條匹配邊,如此交替。如果先手走到了一個未匹配點,說明找到了增廣路,與最大矛盾,所以先手走到的點一定是匹配點,此時後手必定可以繼續,所以後手必勝。

如何判斷一個點是否在最大匹配中:
在這裏插入圖片描述


回到HDU3446 daizhenyang’s chess

題目描述:

給出棋盤上每個點的可達點,一個棋子,兩人輪流走,不能走走過的,不能走的人輸,問先手是否必勝。
n,m15n,m\le 15

題目分析:

問題相當於是把二分圖變成了一般圖,上面對於是否必勝的分析仍然是適用的,即 起點是否一定在最大匹配中,但是一般圖無法像二分圖一樣求關鍵點,但是這個題只有一個起點,所以我們先求出全圖的最大匹配,然後刪去起點,再求出最大匹配與原來的比較即可。
或者可以先求出刪去起點的最大匹配,然後加入起點,看是否存在增廣路。

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");
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章