最基础的anti-sg
结论:
当且仅当:
(1)所有堆的石子数都是1且游戏的sg值为0
(2)有些堆得石子数大于1且游戏的sg值不为0
先手胜。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<set>
#include<string>
#include<cstring>
#define ll long long
using namespace std;
int main(){
int t,n,a;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
int flag=0,ans=0;
for(int i=1;i<=n;i++){
scanf("%d",&a);
ans^=a;
if(a!=1)flag=1;
}
if((flag&&ans)||(!flag&&ans==0)){
printf("John\n");
}
else printf("Brother\n");
}
}
题意:
n堆石子,每堆石子排成一行,每次可以连续取1~n个,也就是说,和从边上取1~n个,取完后剩下的还是1堆,或者从中间取,这样会剩下两堆(这区别普通nim,和普通的multi-sg),我们先打表找规律,然后发现sg[n]=n,接着直接求解
sj定理:
先手必胜当且仅当:
(1)游戏的sg函数不为0,且游戏中某一单一游戏的sg函数大于1
(2)游戏的sg函数为0,且游戏中没有单一游戏的sg函数大于1
打表代码:
void get_sg(){
sg[0]=0;
sg[1]=1;
for(int i=1;i<=105;i++){//有这么多石子
int vis[106]={0};
for(int j=0;j<i;j++){//剩j个石子
vis[sg[j]]=1;
}
for(int j=1;j<i;j++){
for(int k=1;k<i;k++){
if(j+k<i)vis[sg[j]^sg[k]]=1;
}
}
for(int j=0;;j++){
if(vis[j]==0){
sg[i]=j;
break;
}
}
}
}
AC代码:
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<set>
#include<string>
#include<cstring>
#define ll long long
using namespace std;
int main(){
int n,a;
while(scanf("%d",&n)!=EOF){
int flag=0,ans=0;
for(int i=1;i<=n;i++){
scanf("%d",&a);//sg[a]=a
ans^=a;
if(a>1)flag=1;
}
if((flag&&ans)||(ans==0&&flag==0)){
printf("Yes\n");
}
else printf("No\n");
}
}