Topcoder SRM637-Div1-Lv2 PathGame
涉及知识点:博弈论 SG函数
题意
\(2\) 行若干列的方格图,格子为黑色\((\#)\)或白色\((.)\),Snuke 与 Sothe 轮流在上面把白格涂黑,如果有一条从左到右的白色路径,游戏继续,否则游戏结束且最后涂色的玩家判输,保证初始有路径,双方皆用最优策略,判断最后胜者。
思路
我们记录 \(f[l][r][i]\) 为长度为 \(i\) 的全白格的 \(SG\) 函数,\(l,r\) 分别表示左右端只能从上方出发\((0)\),只能从下方出发\((1)\),上下均可出发\((2)\),列举长度为 \(1\) 时最基本的情况赋初值,枚举长度,再枚举中间的断点(涂黑的块)进行转移,显然中间涂黑块所在列 \(SG\) 函数为 \(0\),而左右两边的子块已在长度更小的情况下处理过,因此根据 \(SG\) 定理异或在一起即可。最后将原方格图根据黑格分段,求亦或和即可。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1005;
int n,f[3][3][MAXN];
string s[3];
bitset<MAXN>ava;
int main(){
cin>>n>>s[1]>>s[2];
n=s[1].size();
f[0][1][1]=0;
f[1][0][1]=0;
f[0][0][1]=1;
f[2][0][1]=1;
f[2][1][1]=1;
f[0][2][1]=1;
f[1][2][1]=1;
f[1][1][1]=1;
f[2][2][1]=1;
for(int i=2;i<=n;i++){
for(int l=0;l<=2;l++){
for(int r=0;r<=2;r++){
ava.reset();
for(int j=0;j<i;j++){
if(!((j==0&&l==1)||(j==i-1&&r==1))) ava[f[l][0][j]^f[0][r][i-j-1]]=1;
if(!((j==0&&l==0)||(j==i-1&&r==0))) ava[f[l][1][j]^f[1][r][i-j-1]]=1;
}
for(int j=0;j<=n;j++){
if(!ava[j]){
f[l][r][i]=j;
break;
}
}
}
}
}
int ans=0,lasti=-1,lastdir=2;
for(int i=0;i<n;i++){
if(s[1][i]=='#') ans^=f[lastdir][1][i-lasti-1],lasti=i,lastdir=1;
else if(s[2][i]=='#') ans^=f[lastdir][0][i-lasti-1],lasti=i,lastdir=0;
}
ans^=f[lastdir][2][n-lasti-1];
if(ans!=0) puts("Snuke");
else puts("Sothe");
return 0;
}