显然是个二分图,设开始位置是左边,另一边是右边
那么先手是把左边挪到右边,后手是把右边挪到左边,不能挪的那方失败
结论:Alice必胜当且仅当开始位置不一定在最大匹配上
必要性:
如果开始位置不在最大匹配上,那一定有种匹配方案不包含开始位置(废话)
考虑这种方案,由于左边已经空出来了,所以右边和它连通的点都已经有匹配。
这样Bob移动到哪里,Alice就移到它的匹配点,这样Alice必胜。
充分性:
考虑逆否命题,如果开始位置一定在最大匹配上,那么Bob必胜。
①如果右边有和开始位置相邻的未匹配点
Bob移到这个位置,然后转换为了上面的情况
②如果没有
那就移到匹配点
因为开始点一定在最大匹配上,所以移了之后找不到未匹配点
同理后面如果左边有未匹配点就找到了一条增广路,矛盾
这样可以一直走匹配点直到胜利
所以先跑个最大匹配,从未匹配点开始dfs,对所有与之相邻点给匹配点打上标记
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#define MAXN 10005
#define MAXM 50005
using namespace std;
struct edge{int u,v;}e[MAXM];
int head[MAXN],nxt[MAXM],cnt;
void addnode(int u,int v)
{
e[++cnt]=(edge){u,v};
nxt[cnt]=head[u];
head[u]=cnt;
}
int n,m;
char s[105][105];
#define id(x,y) (((x)-1)*m+(y))
const int dx[]={-1,1,0,0},dy[]={0,0,-1,1};
int link[MAXN],used[MAXN];
bool find(int u,int mark)
{
for (int i=head[u];i;i=nxt[i])
if (used[e[i].v]!=mark)
{
used[e[i].v]=mark;
if (!link[e[i].v]||find(link[e[i].v],mark)) return link[e[i].v]=u,true;
}
return false;
}
bool ans[MAXN];
void dfs(int u)
{
for (int i=head[u];i;i=nxt[i])
if (!ans[link[e[i].v]])
{
ans[link[e[i].v]]=true;
dfs(link[e[i].v]);
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%s",s[i]+1);
for (int x=1;x<=n;x++)
for (int y=1;y<=m;y++)
if (s[x][y]=='.'&&(x+y)&1)
for (int i=0;i<4;i++)
if (s[x+dx[i]][y+dy[i]]=='.')
addnode(id(x,y),id(x+dx[i],y+dy[i]));
int d=0;
for (int x=1;x<=n;x++)
for (int y=1;y<=m;y++)
if (s[x][y]=='.'&&(x+y)&1)
d+=find(id(x,y),id(x,y)),ans[id(x,y)]=1;
cerr<<d<<endl;
for (int x=1;x<=n;x++)
for (int y=1;y<=m;y++)
if (s[x][y]=='.'&&!((x+y)&1))
link[id(x,y)]? ans[link[id(x,y)]]=0,link[link[id(x,y)]]=id(x,y):ans[id(x,y)]=1;
int tcnt=cnt;
for (int i=1;i<=tcnt;i++) addnode(e[i].v,e[i].u);
for (int i=1;i<=n*m;i++) if (ans[i]) dfs(i);
int tot=0;
for (int x=1;x<=n;x++)
for (int y=1;y<=m;y++)
tot+=ans[id(x,y)];
printf("%d\n",tot);
for (int x=1;x<=n;x++)
for (int y=1;y<=m;y++)
if (ans[id(x,y)])
printf("%d %d\n",x,y);
return 0;
}