【BZOJ2437】【NOI2011】兔兔与蛋蛋(博弈论,二分图匹配)

【BZOJ2437】【NOI2011】兔兔与蛋蛋(博弈论,二分图匹配)

题面

BZOJ

题解

考虑一下暴力吧。
对于每个状态,无非就是要考虑它是否是必胜状态
这个直接用\(dfs\)爆搜即可。
这样子对于每一次操作,考虑兔兔操作后的状态是否是必胜状态
如果这个状态是必胜状态,并且蛋蛋操作完后的状态是(兔兔的)必败状态
那么这就是一个“犯错误”的操作。
这样暴力可以拿到\(75pts\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 45
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int n,m,X,Y;
char ch[MAX];
int g[MAX][MAX],zt[MAX];
int d[4][2]={1,0,-1,0,0,1,0,-1};
int ans[MAX*MAX],top,Q;
bool dfs(int x,int y,int z)
{
	for(int i=0;i<4;++i)
	{
		int xx=x+d[i][0],yy=y+d[i][1];
		if(xx<1||xx>n||yy<1||yy>m||g[xx][yy]!=z)continue;
		swap(g[x][y],g[xx][yy]);
		if(!dfs(xx,yy,z^1)){swap(g[x][y],g[xx][yy]);return true;}
		swap(g[x][y],g[xx][yy]);
	}
	return false;
}
int main()
{
	n=read();m=read();
	for(int i=1;i<=n;++i)
	{
		scanf("%s",ch+1);
		for(int j=1;j<=m;++j)
			if(ch[j]=='X')g[i][j]=1;
			else if(ch[j]=='O')g[i][j]=0;
			else if(ch[j]=='.')g[i][j]=2;
	}
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			if(g[i][j]==2){X=i;Y=j;break;}
	Q=read();
	for(int i=1,x,y;i<=Q;++i)
	{
		x=read(),y=read();
		zt[i]=dfs(X,Y,0);
		swap(g[x][y],g[X][Y]);
		X=x;Y=y;
		if(zt[i]&&dfs(X,Y,1))ans[++top]=i;
		x=read();y=read();
		swap(g[x][y],g[X][Y]);
		X=x;Y=y;
	}
	printf("%d\n",top);
	for(int i=1;i<=top;++i)printf("%d\n",ans[i]);
	return 0;
}


观察一下基本的事实。
考虑走的方案是否可能出现一个环。
无论环有多大,似乎都是一样的,所以我们就考虑在\(2\times 2\)的方格中移动
初始时空格在\((1,1)\),它和\((1,2)\)交换位置,此时,\((1,1)\)为白
然后\((1,2)\)\((2,2)\)交换位置,\((1,2)\)为黑
\((2,2)\)\((2,1)\)交换位置,\((2,2)\)为白
此时如果\((2,1)\)能与\((1,1)\)交换位置,那么\((1,1)\)需要是黑色
但是\((1,1)\)是白色,所以显然不能成环。
对于一个更大的环,无非是长\(+1\)或者宽\(+1\)拓展出来的,每次多走两步,对于黑白没有影响。

既然不能成环,意味着每个点只会被经过一次。
那么,我们可以重新开一下这个过程,可以理解为从空格开始,
走一条路径,路径上黑白相间。
黑白相间?有点像二分图的感觉。每条增广路不就是黑白相间吗?
因为先手的是白格子,所以可以把空格开成黑格子
这样子就是要从这个黑格子这里找一条增广路出去。
再考虑一下胜利的情况,如果先手胜利,那么从黑格子连向了一个白格子
然后找不到增广路了,此时白格子胜。
继续把这个情况向上拓展,我们可以得到。

如果当前点一定在二分图的最大匹配中,那么先手必胜。因为先手始终可以沿着最大匹配的匹配边走,而最大匹配中交错路的数量为奇数条,也就是进行奇数次操作,意味着后手最后无法操作,此时先手必胜。

那么,每次进行判定当前点是否在二分图的最大匹配中,是否一定被选中即可判定先手是否必胜,依次可以计算答案。

至于如何计算当前点是否一定在二分图的最大匹配中?
把当前点给\(ban\)掉,在增广的时候强行不选,然后对其匹配点进行增广,
如果能够找到新的增广路,意为这当前点可以被替代,
否则当前点一定在最大匹配中。
这题好神仙啊

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 45
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int n,m,X,Y;
char ch[MAX];
int g[MAX][MAX],zt[MAX*MAX];
int d[4][2]={1,0,-1,0,0,1,0,-1};
int ans[MAX*MAX],top,Q;
int bh[MAX][MAX],tot;
struct Line{int v,next;}e[MAX*MAX<<3];
int h[MAX*MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int match[MAX*MAX],tim,vis[MAX*MAX];
bool ban[MAX*MAX];
bool dfs(int u)
{
	if(ban[u])return false;
	for(int i=h[u];i;i=e[i].next)
		if(vis[e[i].v]!=tim&&!ban[e[i].v])
		{
			vis[e[i].v]=tim;
			if(!match[e[i].v]||dfs(match[e[i].v]))
			{
				match[e[i].v]=u;match[u]=e[i].v;
				return true;
			}
		}
	return false;
}
int main()
{
	n=read();m=read();
	for(int i=1;i<=n;++i)
	{
		scanf("%s",ch+1);
		for(int j=1;j<=m;++j)
			if(ch[j]=='X')g[i][j]=1;
			else if(ch[j]=='O')g[i][j]=0;
			else if(ch[j]=='.')g[i][j]=1,X=i,Y=j;
	}
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			bh[i][j]=++tot;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			if(g[i][j])
				for(int k=0;k<4;++k)
				{
					int x=i+d[k][0],y=j+d[k][1];
					if(x<1||x>n||y<1||y>m||g[x][y])continue;
					Add(bh[i][j],bh[x][y]);
					Add(bh[x][y],bh[i][j]);
				}
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			if(g[i][j])++tim,dfs(bh[i][j]);
	Q=read();
	for(int i=1,id;i<=Q+Q;++i)
	{
		id=bh[X][Y];ban[id]=true;
		if(match[id])
		{
			int nw=match[id];match[nw]=match[id]=0;
			++tim;zt[i]=!dfs(nw);
		}
		X=read();Y=read();
	}
	for(int i=1;i<=Q;++i)
		if(zt[i+i-1]&zt[i+i])ans[++top]=i;
	printf("%d\n",top);
	for(int i=1;i<=top;++i)printf("%d\n",ans[i]);
	return 0;
}

posted @ 2018-06-16 22:27  小蒟蒻yyb  阅读(430)  评论(0编辑  收藏  举报