1. 程式人生 > 其它 >[loj6033]棋盤遊戲

[loj6033]棋盤遊戲

將棋盤黑白染色,即構成一張二分圖

將狀態用一張二分圖$G$和一個點$x\in V$描述(分別為仍未被經過的點的匯出子圖和當前棋子所在位置),並稱將要移動棋子的一方為先手

結論:先手必勝當且僅當$x$一定在$G$的最大匹配中

對該結論歸納,顯然$|V|\le 2$時顯然成立

若$|V|<n$時成立,考慮$|V|=n$時——

若$x$一定在最大匹配中,先手任選一組最大匹配並將棋子移動到匹配的點

注意到將新圖(指刪去$x$後)的最大匹配數恰比原圖少1(否則即存在最大匹配不包含$x$),因此先手所選的最大匹配去掉$x$所在的匹配後也為新圖的最大匹配

其並不包含棋子所在的點,根據歸納假設即先手必勝

若$x$不一定在最大匹配中,那麼新圖的最大匹配數和原圖相同

注意到如果新圖的最大匹配不包含某個與$x$相鄰的點,那麼原圖的最大匹配就可以在此基礎上增加$x$和該點的匹配,即與兩者最大匹配數相同矛盾

因此,所有與$x$相鄰的點都一定在新圖的最大匹配中,根據歸納假設即先手必敗

綜上,即得證

由此,對其先任求一組最大匹配,不在匹配中的點即一定在答案中,對匹配中的點再對其所匹配的尋找增廣路來判斷是否一定在最大匹配中

時間複雜度為$o(n^{2}m^{2})$,可以通過

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define
N 105 4 struct Edge{ 5 int nex,to; 6 }edge[N*N*4]; 7 int n,m,V,E,tot,id[N][N],head[N*N],vis[N*N],match[N*N],ans[N*N]; 8 char s[N][N]; 9 void add(int x,int y){ 10 edge[E].nex=head[x]; 11 edge[E].to=y; 12 head[x]=E++; 13 } 14 bool dfs(int k){ 15 if (vis[k])return 0; 16 vis[k]=1
; 17 for(int i=head[k];i!=-1;i=edge[i].nex) 18 if ((!match[edge[i].to])||(dfs(match[edge[i].to]))){ 19 match[k]=edge[i].to; 20 match[edge[i].to]=k; 21 return 1; 22 } 23 return 0; 24 } 25 int main(){ 26 scanf("%d%d",&n,&m); 27 for(int i=0;i<n;i++){ 28 scanf("%s",s[i]); 29 for(int j=0;j<m;j++) 30 if (s[i][j]!='#')id[i][j]=++V; 31 } 32 memset(head,-1,sizeof(head)); 33 for(int i=0;i<n;i++) 34 for(int j=0;j<m;j++) 35 if (id[i][j]){ 36 if ((i)&&(id[i-1][j])){ 37 add(id[i][j],id[i-1][j]); 38 add(id[i-1][j],id[i][j]); 39 } 40 if ((j)&&(id[i][j-1])){ 41 add(id[i][j],id[i][j-1]); 42 add(id[i][j-1],id[i][j]); 43 } 44 } 45 for(int i=1;i<=V;i++) 46 if (!match[i]){ 47 memset(vis,0,sizeof(vis)); 48 dfs(i); 49 } 50 for(int i=1;i<=V;i++) 51 if (!match[i])ans[i]=1; 52 else{ 53 memset(vis,0,sizeof(vis)); 54 ans[i]=dfs(match[i]); 55 if (ans[i])match[i]=0; 56 } 57 for(int i=0;i<n;i++) 58 for(int j=0;j<m;j++) 59 if ((id[i][j])&&(ans[id[i][j]]))tot++; 60 printf("%d\n",tot); 61 for(int i=0;i<n;i++) 62 for(int j=0;j<m;j++) 63 if ((id[i][j])&&(ans[id[i][j]]))printf("%d %d\n",i+1,j+1); 64 }
View Code