ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[JSOI2009] 游戏

2021-10-28 19:34:25  阅读:202  来源: 互联网

标签:二分 必选 游戏 增广 必败 JSOI2009 int 匹配


一、题目

点此看题

二、解法

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

一看就用不了 \(\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);
	}
}

标签:二分,必选,游戏,增广,必败,JSOI2009,int,匹配
来源: https://www.cnblogs.com/C202044zxy/p/15477325.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有