#define P(positive) 必胜态
#define N(negative) 必败态
巴什博奕(Bash Game)
有一堆石子n个 , 每次可以取1~m个石子 , 没有石子可取的那方输 , 问第一个取的人的输赢
给对面一个m+1的堆 , 无论对面取多少 , 你都可以取一个数使这堆石子取完
所以说你要设法取一个数使堆剩下 给对面
int main(){
LL t;sf(t);
while(t--){
LL n,m;sf(n),sf(m);
if(n%(m+1))printf("first\n");//可以把k*(m+1)这个状态给对面
else printf("second\n");//到自己手上的状态是k*(m+1)
}
}
变形(最后取的输) :
只要留一块给对面就赢了,所以判断(n-1)%(m+1)
威佐夫博奕(Wythoff Game)
有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
我们用 表示两堆物品的数量并称其为局势,显然(0,0)为必输局势,这种必输局势我们称为奇异局势。
前几个奇异局势是:(0,0),(1,2),(3,5),(4,7),(6,10),(8,13),(9,15),(11,18),(12,20)
a[k]为前k-1中的a[i]b[i]中未出现的最小非负整数 , b[k]=a[k]+k (a[0]=b[0]=0)
公式 :
性质 :
奇异局势判断 :
直接判断小的那堆能不能写成上面a的公式
int main(){
int a,b;
while(scanf("%d%d",&a,&b)!=EOF){
if(a>b)swap(a,b);
int k=b-a;
int ans=(a!=round(k*(1.0+sqrt(5.0))/2));
printf("%d\n",ans);//0必败态 1必胜态
}
return 0;
}
尼姆博奕(Nimm Game)
有k堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
对于多堆石子 , 如果所有都是0 , 那么是一个N态
当多堆石子异或后结果为0 , 则是一个N态 , 否则为P态4
int main(){
int m,ans,n;
while(~scanf("%d",&m)){
n=ans=0;
while(m--)
scanf("%d",&n),ans^=n,printf("ans=%d\n",ans);
if(ans)printf("Yes\n");
else printf("No\n");
}
}
证明: 当你拿到一个异或为0的状态(没取完) , 取任意一个数 , 这个数因为不能是0 , 所以在二进制中一定有几位会是1 , 也就是说剩下的异或后不会是0即还有石子没取完 而可以证明对方可以取一个数使剩下的石子数异或为0 , 即:维护异或为0的状态
|
斐波那契博弈(Fibonacci Game)
有一堆石子,两个人轮流从其中取走一定的石子,取走最后所有石子的人为赢家,不过得遵循如下规则:
1.第一次取不能取完,至少取1颗.
2.从第二次开始,每个人取的石子数至少为1,至多为对手刚取的石子数的两倍。
当n为Fibonacci数时,先手必败。否则先手必败
证明:根据“Zeckendorf定理”(齐肯多夫定理):任何正整数可以表示为若干个不连续的Fibonacci数之和。如n=83 = 55+21+5+2,假如先手取2颗,那么后手无法取5颗或更多,而5是一个Fibonacci数,那么一定是先手取走这5颗石子中的最后一颗,同理,接下去先手取走接下来的后21颗中的最后一颗,再取走后55颗中的最后一颗,那么先手赢。
反之:如果n是Fibonacci数,如n=89:记先手一开始所取的石子数为y
(1)若y>=34颗(也就是89的向前两项),那么一定后手赢,因为89-34=55=34+21<2*34。
(2)y<34时剩下的石子数x介于55到89之间,它一定不是一个Fibonacci数,把x分解成Fibonacci数:x=55+f[i]+…+f[j],若,如果f[j]<=2y,那么对B就是面临x局面的先手,所以根据之前的分析,后手只要先取f[j]个即可,以后再按之前的分析就可保证必胜。