将棋盘黑白染色,即构成一张二分图
将状态用一张二分图$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 }