[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);
}
}