[JSOI2009] 游戏

一、题目

点此看题

二、解法

真的神题,我至今不知道为什么要联想到最大匹配,说实话评个黑不过分吧

一看就用不了 \(\tt sg\) 函数,这启示我们要去找稳态。考虑行走的过程可以看成二分图上增广的过程,利用完美匹配后不存在增广路这一性质,我们把行走放在二分图上思考。

对原图黑白染色之后把非障碍点连边建立二分图,先用匈牙利算法跑出最大匹配。

结论:如果存在一个完美匹配使得某个点不在匹配点集中,那么这个点是必败态

证明:我们就考虑这个特殊的完美匹配,先手一定走到一个匹配点,然后后手走到这个点对应的点,因为不能走回头路,所以下一次先手必然走到一个新的匹配点,如果走到一个未匹配点那么就产生了增广路,与完美匹配的条件矛盾。

这样我们就找到了稳态,考虑路径长度一定是偶数,那么先手必败。

称这样的点为非必选点,可见非必选点是必败态的充分条件。我们可以证明必选点(在所有完美匹配都出现)是必胜态的充分条件,就可以证明非必选点是必败态的充要条件。

证明:我们考虑必选点如果有边连向非必选点,那么就一步走到必败态,先手必胜。

否则必选点一定只能走到必选点,那么我们把这个点删除,然后把对应的点变成非必选点,向得到的新二分图应用非必选点必败的结论,那么一步走到必败态。

现在的问题变成了找到所有非必选点,首先匈牙利的未匹配点一定是非必选点。然后我们从这些点开始搜索,如果找到了一条非匹配边-匹配边-非匹配边-匹配边...的路径就说明路径上都是非匹配点,时间复杂度 \(O(n^2m^2)\)

三、总结

不能走回头路的问题可以类比增广,找到最大匹配就能获得没有增广路的条件。

#include <cstdio>
#include <algorithm>
using namespace std;
const int M = 10005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,sum,ans,tot,f[M],b[M],c[M],p[M],vis[M];
int w,d[M],dx[4]={-1,1},dy[4]={0,0,1,-1};char s[M];
struct edge
{
	int v,next;
}e[10*M];
int id(int x,int y)
{
	return (x-1)*m+y;
}
void add(int u,int v)
{
	e[++tot]=edge{u,f[v]},f[v]=tot;
}
int dfs(int u)
{
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(vis[v]==k) continue;
		vis[v]=k;
		if(!p[v] || dfs(p[v]))
		{
			p[v]=u,p[u]=v;
			return 1;
		}
	}
	return 0;
}
void dfs2(int u)
{
	vis[u]=k;
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(p[v]==0 || p[v]==u || vis[p[v]]==k)
			continue;
		d[++w]=p[v];
		dfs2(p[v]);
	}
}
signed main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s+1);
		for(int j=1;j<=m;j++)
		{
			b[id(i,j)]=s[j]=='#';
			c[id(i,j)]=(i+j)&1;
			sum+=(s[j]=='.');
		}
	}
	//build the edge
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++) if(!b[id(i,j)])
	for(int k=0;k<4;k++)
	{
		int x=i+dx[k],y=j+dy[k];
		if(x<1 || y<1 || x>n || y>m || b[id(x,y)])
			continue;
		add(id(i,j),id(x,y));
	}
	int o=n*m;
	for(int i=1;i<=o;i++) if(!b[i] && c[i])
		k++,ans+=dfs(i);
	if(sum%2==0 && sum/2==ans)
	{
		puts("LOSE");
		return 0;
	}
	k++;
	for(int i=1;i<=o;i++) if(!p[i] && !b[i])
		d[++w]=i,dfs2(i);
	puts("WIN");
	sort(d+1,d+1+w);
	for(int i=1;i<=w;i++)
	{
		int x=(d[i]-1)/m+1,y=(d[i]-1)%m+1;
		printf("%d %d\n",x,y);
	}
}
posted @ 2021-10-28 19:19  C202044zxy  阅读(64)  评论(0编辑  收藏  举报