^^^转载请注明出处,谢谢合作O(∩_∩)O~
取(2堆)石子游戏
Problem Description
有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。如果你胜,你第1次怎样取子?
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<iostream> using namespace std; #include<math.h> #define M 1500000 //大于10W的数组要定义为全局变量 M注意 一般不要写成1000+10 应该加上括号 或直接写成一个数 int d[3*M]={0}; //注意:此处别写成d[M] 因为后面的用到的数肯定要大于M int z[M/2][2]={0}; int main() { int a,b,a1; int i,j,k,k1; int m=1,p,q; for(k=1;k<M/2;k++) { for(j=m;;j++) { if(d[j]==0)break; //第j个数还没有用到 所以开始用此数 } z[k][0]=j; z[k][1]=j+k; d[j+k]=j+k; //其中d[j]就等于j,为0,1,2,3,4...的自然数 m=j+1; } while(scanf("%d%d",&a,&b)!=EOF&&!(a==0&&b==0)) { p=0; q=0; k=b-a; a1=(int)((sqrt(5.0)+1)/2*k); if(a==a1)printf("0\n"); else { printf("1\n"); for(i=0;i<M/2;i++) //a=ak { if(z[i][0]==a) { k=i; p=1; break; } } for(i=0;i<M/2;i++)//b=bk { if(z[i][1]==b) { k1=i; q=1; break; } } if(p) { //1 if(b<a+k) //包括a=b的情况 { printf("%d %d\n",z[b-a][0],z[b-a][1]);//输出同时从2堆取得 } //2 else { printf("%d %d\n",z[k][0],z[k][1]); } }//if(p) if(p==0&&q==0) {//3 printf("%d %d\n",z[b-a][0],z[b-a][1]); //4 for(i=0;i<M/2;i++)//a!=ak且b!=bk { if(z[i][1]==a) { j=i; break; } } printf("%d %d\n",z[j][0],z[j][1]); }//if(p==0&7q==0) //5 if(q) { if(a==b)printf("0 0\n");//当b=bk时,a=b的情况 if(a>z[k1][0]) { printf("%d %d\n",z[k1][0],z[k1][1]); } //6 else { for(i=0;i<M/2;i++) { if(z[i][0]==a) { j=i; break; } if(z[i][1]==a) { j=i; break; } } //两种小情况可以一起输出 printf("%d %d\n",z[j][0],z[j][1]); } }//if(q) }//else } return 0; }