[Noi2011]兔兔与蛋蛋


题目

题解

容易想到空格移动的路径是不会自交的。

因为空格移动的路径是黑白棋相间的

所以对棋盘进行黑白染色,建立二分图

如果黑白两格上的棋子不一样则可以连边

如果一个人(A)将空格移入了一个在最大匹配内的点,那么它的对手(B)就可以沿着匹配边前进(否则就相当于找到了一条新的匹配边)

而A只能沿着非匹配边前进,这样到最后肯定是A无路可走(否则就相当于找到了一条新的匹配边)

所以如果起始点不在最大匹配中那么先手只能走非匹配边,即必败。

另外有可能起始点在最大匹配中,但后手可以沿着另一个最大匹配前进。

所以必须保证起始点必须在最大匹配中。

具体的话可以给这个点打个删除标记,看看它的原匹配点能不能找到另一个对象。

形成代码就是先跑一边匈牙利

然后对于每个操作(把兔兔与蛋蛋的拆开),把经过的点全部删除,看看空格是否必须在最大匹配中。

注意

每执行完一次操作要清vis

黑白染色时相邻行要错开

#include 
#include 
#include 
#include 
#include 
using namespace std;
#define N 2000
#define pos(i,j) ((i-1)*m+j)
char map[50][50];
vector vec[N];
int mx[4]={0,0,1,-1},my[4]={1,-1,0,0},p[N],vis[N],match[N],win[N],n,m;
bool disable[N];
void build(int i,int j)
{
	for(int k=0;k<4;k++)
	{
		int tx=i+mx[k],ty=j+my[k];
		if(abs(map[i][j]-map[tx][ty])==1) vec[pos(i,j)].push_back(pos(tx,ty));
	}
}
bool dfs(int id)
{
	for(int i=0;i>n>>m;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf(" %c",&map[i][j]);
			if(map[i][j]=='.') sx=i,sy=j,map[i][j]=3;
			else if(map[i][j]=='X') map[i][j]=3;
			else map[i][j]=2;
		}
	}
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) build(i,j);//建边
	hungary();
	cin>>step;
	for(int i=1;i<=step*2;i++)
	{
		memset(vis,0,sizeof(vis));
		disable[pos(sx,sy)]=true;
		//win[i]:在当前状态下是否有必胜策略
		win[i]=(match[pos(sx,sy)])&&(!dfs(match[pos(sx,sy)]));//如果空格不必须在最大匹配中
		if(win[i]) match[match[pos(sx,sy)]]=0;//释放匹配的点
		scanf("%d%d",&sx,&sy);
	}
	int cnt=0;
	for(int i=1;i