Luogu P10501 Cutting Game 题解
博弈论经典题目,考虑使用 SG 函数解决。
但是这一题和有向图游戏的定义不同,在有向图游戏不能操作者判负,而这一题中操作出某个状态者判胜。因此,我们需要进行转化。经典做法是往前推直到推出必败态作为有向图游戏的终点。
我们考虑什么样的情况可以剪出 \(1\times 1\) 的纸条,显然,当且仅当在 \(1\times n\) 或 \(n\times 1\) 的纸条上可以剪出 \(1\times 1\) 的纸条。因此,这种情况下是先手必胜态。
往前推出必败态,就是要找到一个只能剪出 \(1\times n\) 或 \(n\times 1\) 的纸条的状态。不难发现在 \(2\times2,2\times3,3\times2\) 的纸条中都只能剪出 \(1\times n\) 或 \(n\times 1\) 的纸条,因此它们是必败态。以它们作为有向图游戏的终点,即可按照有向图游戏的方式处理。
我们考虑枚举每个状态在哪一行或者哪一列剪开,之后就是两个剪纸子问题,求出它们的 SG 函数的值后用有向图游戏和异或起来就是当前状态的一个后继状态的 SG 函数值,求 mex 就行了。
注意每一步不会直接剪出 \(1\times n\) 或 \(n\times 1\) 的纸条,因为会使对手直接获胜,所以代码实现时需要注意边界条件。下面给出转移式。
\[sg(n,m)=\operatorname{mex}\{\{sg(i,m)\oplus sg(n-i,m)\mid 2\le i\lt n-1\}\cup\{sg(n,i)\oplus sg(n,m-i)\mid 2\le i\lt m-1\}\}
\]
使用记忆化搜索预处理实现。
#include <bits/stdc++.h>
using namespace std;
long long n,m,x[300000],sg[300][300];
bool vis[300][300];
long long dfs(long long n,long long m)
{
if(n==2&&m==2)return 0;
if(n==2&&m==3)return 0;
if(n==3&&m==2)return 0;
if(vis[n][m])return sg[n][m];
vis[n][m]=1;
for(int i=2;i<n-1;i++)x[dfs(i,m)^dfs(n-i,m)]=1;
for(int i=2;i<m-1;i++)x[dfs(n,i)^dfs(n,m-i)]=1;
long long mex=0;
while(x[mex])mex++;
for(int i=2;i<n-1;i++)x[dfs(i,m)^dfs(n-i,m)]=0;
for(int i=2;i<m-1;i++)x[dfs(n,i)^dfs(n,m-i)]=0;
return sg[n][m]=mex;
}
int main()
{
for(int i=2;i<=200;i++)
for(int j=2;j<=200;j++)dfs(i,j);
while(scanf("%lld%lld",&n,&m)!=-1)
{
if(sg[n][m])printf("WIN\n");
else printf("LOSE\n");
}
return 0;
}

浙公网安备 33010602011771号