威佐夫博弈
博弈规则:有两堆各若干个物品,两个人轮流从某一堆取至少一个或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
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是否符合公式就可以了。
代码:
#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; }