博弈规则:有两堆各若干个物品,两个人轮流从某一堆取至少一个或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
a堆和b堆的个数用(a,b)表示
奇异局势:面对的时候,就是输家,比如(0,0),(1,2),(3,5),(4,7),(6,10)...
举例(0,0),规则上说取光者胜,轮到你的时候是(0,0),说明你输了。
举例(1,2),如果拿走一个,变成(1,1)或者(0,2),对手可以同时从两堆拿1个或者b堆拿2个,你都会输,如果拿走两个,变成(0,1)或者(1,0),对手随便拿走一个,你都会输。面对(1,2),必输。
举例(3,5),如果拿走一个,变成(2,5)或者(3,4),对手可以拿走b堆2个变成(2,1),让你面对奇异局势,也可以同时从a、b堆拿走2个,变成(1,2)让你面对奇异局势。如果拿走2个,可以变成(1,5),(2,4),(3,2),根据规则对手可以变成奇异局势(1,2),(2,1),(1,2)。必输...
任何非奇异局势都可以转变成奇异局势,先手面对的如果是奇异局势必输,如果不是奇异局势必赢。那么如何判断奇异局势呢?a =[k(1+√5)/2],b= a + k (k=0,1,2,...n 方括号表示取整函数)
当k=0时,有(0,0)
当k=1时,有(1,2)
当k=2时,有(3,5)
当k=3时,有(4,7)
当k=4时,有(6,10)
......
观察一下可以发现,每个自然数只出现过一次,并且奇异局势的两个数的差就是k值,所以只要判断a是否符合公式就可以了。
题目:
Input
输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,且a<=b。a=b=0退出。
Output
输出也有若干行,如果最后你是败者,则为0,反之,输出1,并输出使你胜的你第1次取石子后剩下的两堆石子的数量x,y,x<=y。如果在任意的一堆中取走石子能胜同时在两堆中同时取走相同数量的石子也能胜,先输出取走相同数量的石子的情况.
Sample Input
1 2 5 8 4 7 2 2 0 0
Sample Output
0 1 4 7 3 5 0 1 0 0 1 2
代码:
#include<stdio.h> #include<iostream> #include<math.h> using namespace std; const double sq=(1+sqrt(5.0))/2.0;///浮点数再取整 int main() { int a,b,c; while(cin>>a>>b&&(a+b)) { c=b-a; if( a==(int)(c*sq) && b==a+c ) cout<<0<<endl; else { cout<<1<<endl; for(int i=1;i<=a;i++)///从两边那走一样多的石头 if( a-i==(int) (c*sq) )///(int)(c*sq)对乘积整体取整 printf("%d %d\n",a-i,b-i); for(int i=1;i<=b-a;i++)///从b堆拿走石头到b堆和a堆一样多,a比b少 if( a==(int) ((b-i-a)*sq) ) printf("%d %d\n",a,b-i); b=a; ///把b的石头数置为a,然后从a堆中拿,a还是比b少 for(int i=1;i<a;i++) if( a-i==(int) ((b-a+i)*sq) ) printf("%d %d\n",a-i,b); } } return 0; }
原文:https://www.cnblogs.com/shoulinniao/p/9478868.html