题目大意:有2个数,2个人轮流把大的数(设为a)减去小的数(设为b)的倍数(不能超过大的数),一直到有一方能把某个数减到0,该方获胜。Stans先走,问最后赢的是谁。
题目分析:
每次减去b的倍数,那么直到不能减b(再减去1个b都会变成负数)时,就进行下2个数的“新”一轮减法了。什么时候这两个数(a,b)不能减(原本b不作为减数)?当a'小于b的时候,其实也就是当a减去(a/b)的时候,这时候a' = a%b。
当局面变成(a',b)时,事实上,大数变成了b,小数变成了a'。是不是觉得很熟悉?对的,这不就是GCD么?
然后每次从大的数(a)中减去x个小的数(b),其实很类似于每次从一共有(a/b)个石子的堆中取x个。—— 当a/b==0时,这堆石子取完;因为a/b == 0时,按照规则,开始进行(b,a%b)了。
但是这和nim还是有区别的,因为这些石子堆是有顺序的,必须先取完第一堆,再取下一堆。
注意的是:我们要从终态(就是下完这一步就整个博弈结束了)往上推。
1 #include<stdio.h>
2 #include<iostream>
3 #include<string.h>
4
5 using namespace std;
6 int s[1111],cnt;
7 void gcd(int a,int b){
8 s[cnt++] = a/b;
9 if(a%b == 0)return ;
10 gcd(b,a%b);
11 }
12
13 int main()
14 {
15 #ifdef LOCALL
16 // freopen("in.txt","r",stdin);
17 //freopen("out2.txt","w",stdout);
18 #endif
19 int n,m;
20 while(cin>>n>>m,n!=0||m!=0){
21 int max = n+m;n = n>m?n:m;m = max - n;
22 if(n%m==0){cout<<"Stan wins\n";continue;}
23 cnt = 0;
24 gcd(n,m);
25 int ans = 0;
26 for(int i = cnt-1;i>=0;i--){
27 if(ans == 0||(ans == 1&&s[i]>1) ) //如果下一步是sg = 0,P,或者下一步原本是sg != 0,N,但能通过取s[i]剩0或者1来控制下一步sg值为0,此次我方能赢
28 ans = 1;
29 else ans = 0;//ans == 1&&s[i]==1的情况不能控制,也就是现在是P:0,我方必败
30 }
31 //cout<<ans<<endl;
32 if(ans)cout<<"Stan wins\n";
33 else cout<<"Ollie wins\n";
34 }
35 return 0;
36 }
为什么这样是对的?
为什么那样是错的?
凭什么这样是最优的?
凭什么那样不是最优的?
为什么没有更优的解法?
就不能找一个更好的解法吗?
若不知其所以然何苦知其然?