【LOJ6033】棋盘游戏【二分图博弈】

传送门

显然是个二分图,设开始位置是左边,另一边是右边

那么先手是把左边挪到右边,后手是把右边挪到左边,不能挪的那方失败

结论: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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值