题意
见到棋盘首先想黑白染色,之后对这个二分图跑一个最大匹配。
假如一个棋子放在了一个非匹配点上,那么先手必败,因为后手每次都有匹配边可以走。
于是我们要求有多少个点可能不在一个最大匹配中:
考虑一个非匹配点(x),假如它连向了一个匹配点(y),那么它就能替换掉当前当前和这个匹配点匹配的点(z),而这时(z)也可以进行同样的操作。
于是我们从每个非匹配(x)出发进行(dfs),途经的和(x)同一集合的点都满足要求。
code:
#include<bits/stdc++.h>
using namespace std;
const int maxn=150;
int n,m,cnt_edge,tim,ans;
int head[maxn*maxn],match[maxn*maxn],vis[maxn*maxn];
char a[maxn][maxn];
bool check[maxn*maxn];
inline int read()
{
char c=getchar();int res=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
struct edge{int to,nxt;}e[maxn*maxn*8];
inline void add_edge(int u,int v)
{
e[++cnt_edge].nxt=head[u];
head[u]=cnt_edge;
e[cnt_edge].to=v;
}
inline int id(int i,int j){return (i-1)*m+j;}
bool dfs(int x)
{
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(vis[y]==tim)continue;
vis[y]=tim;
if(!match[y]||dfs(match[y])){match[y]=x;match[x]=y;return 1;}
}
return 0;
}
void dfs_ans(int x)
{
check[x]=1;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(!check[match[y]])dfs_ans(match[y]);
}
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++)scanf("%s",a[i]+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(a[i][j]=='#')continue;
if(i<n&&a[i+1][j]=='.')add_edge(id(i,j),id(i+1,j)),add_edge(id(i+1,j),id(i,j));
if(j<m&&a[i][j+1]=='.')add_edge(id(i,j),id(i,j+1)),add_edge(id(i,j+1),id(i,j));
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(((i+j)&1)&&a[i][j]=='.')
tim++,dfs(id(i,j));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(a[i][j]=='.'&&!match[id(i,j)]&&!check[id(i,j)])
dfs_ans(id(i,j));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(check[id(i,j)])ans++;
printf("%d
",ans);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(check[id(i,j)])printf("%d %d
",i,j);
return 0;
}