题目:https://vjudge.net/problem/UVA-10561
由于禁区内谁放谁输,所以禁区之外的一段一段分成了很多子游戏。
子游戏g(0)=0,g(1)=g(2)=g(3)=1,超过3以后就可以分成子游戏了,比如连续4个格子的,若在最左边放一个。
那么该格子可以影响最左边的3个格子,只有最右边的一个格子不受影响,所以g(4)=mex{g(x-3),g(x-4)}=2...依次类推
注意,当连续格子数大于5个得时候,要考虑子游戏情况。例如连续的6个格子,若放置的格子影响的是后5个格子,
则mex中有一项为g(1)xor g(x-6)。
最终,可以计算出g(x)函数,利用Nim和可求得结果。
#include<bits/stdc++.h>
using namespace std;
#define maxn 205
int g[maxn];
int vis[maxn],win[maxn],lis[maxn];
int a[maxn],n;
char now[maxn];
void caculate(){
int j;
g[0]=0,g[1]=g[2]=g[3]=1;
g[4]=g[5]=2;
for(int i=6;i<=maxn;i++){
memset(vis,0,sizeof(vis));
vis[g[i-3]]=vis[g[i-4]]=vis[g[i-5]]=1;
for(int j=1;j+5<=i-j;j++)
vis[g[j]^g[i-j-5]]=1;
for( j=0;vis[j];j++) ;
g[i]=j;
}
}
bool check(){
int i, j, c, nim_sum=0;
memset(a,0,sizeof(a));
for(i=2;i<n;i++)if(now[i]==now[i-1] and now[i]==now[i+1] and now[i]=='X')return true;
for(i=2;i<n;i++)
{
c=(now[i-1]=='X')+(now[i]=='X')+(now[i+1]=='X');
if(c==2)return false;
}
for(i=1;i<=n;i++)
if(now[i]=='X')
for(j=0;j<3;j++)
{
if(i+j<=n)a[i+j]=1;
if(i-j>0)a[i-j]=1;
}
for(int i=1;i<=n;i=j+1){
if(a[i]) {j=i;continue;}
else{
for(;a[j+1]==0 and j<n;j++) ;
nim_sum^=g[j+1-i];
}
}
return nim_sum==0;
}
int main(){
int i,j,t,a=0;
caculate();
scanf("%d",&t);
while(t--){
scanf("%s",now+1);
a=0;
n=strlen(now+1);
memset(win,0,sizeof(win));
for(int i=1;i<=n;i++)
if(now[i]=='.'){
now[i]='X';
if(check())
win[++a]=i;
now[i]='.';
}
if(win[1])
{
printf("WINNING\n%d",win[1]);
for(int i=2;win[i];i++)
printf(" %d",win[i]);
}
if(win[1]==0) printf("LOSING\n");
putchar(10);
}
}