http://poj.org/problem?id=2348
顺便说,必应翻译真的好用,比谷歌翻译好用100倍。
很难判断这道题的具体博弈类型。
有两种写法,一种是找规律,一种是推理得到关系后循环(或递归)处理。两种写法都能在题目下面的discuss中找到。
1.找规律,我在这里直接复制了discuss中大神算出的sg函数表(在考试中这种写法是很值得借鉴的,这里就体现出代码能力的重要了,找规律天下第一!)。
我算了一下前 30 × 30 的 Sprague-Grundy 函数表,如下:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
-----------------------------------------------------------------------------------------------
0 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
2 | 0 2 1 0 2 1 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15
3 | 0 3 0 1 0 1 2 1 2 3 2 3 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8 9 9 9 10
4 | 0 4 2 0 1 0 0 1 2 1 1 2 3 2 3 3 4 3 4 4 5 5 5 5 6 6 6 6 7 7 7
5 | 0 5 1 1 0 1 0 0 0 1 2 1 2 2 2 3 2 3 3 3 4 3 4 4 4 5 4 5 5 5 6
6 | 0 6 3 2 0 0 1 0 0 0 1 1 2 1 1 1 2 2 3 2 2 3 3 3 4 3 4 4 4 4 5
7 | 0 7 3 1 1 0 0 1 0 0 0 0 1 1 2 1 1 2 2 2 2 3 2 2 3 3 3 3 4 3 4
8 | 0 8 4 2 2 0 0 0 1 0 0 0 0 1 1 1 2 1 1 1 1 2 2 2 3 2 2 3 3 3 3
9 | 0 9 4 3 1 1 0 0 0 1 0 0 0 0 0 1 1 1 2 1 1 1 2 2 2 2 2 3 2 2 2
10 | 0 10 5 2 1 2 1 0 0 0 1 0 0 0 0 0 0 1 1 1 2 1 1 1 2 1 2 2 2 2 3
11 | 0 11 5 3 2 1 1 0 0 0 0 1 0 0 0 0 0 0 1 1 1 1 2 1 1 1 1 2 2 2 2
12 | 0 12 6 4 3 2 2 1 0 0 0 0 1 0 0 0 0 0 0 0 1 1 1 1 2 1 1 1 1 1 1
13 | 0 13 6 4 2 2 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 1 2 1 1 1 1
14 | 0 14 7 4 3 2 1 2 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 1 1 2 1 1
15 | 0 15 7 5 3 3 1 1 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 2
16 | 0 16 8 5 4 2 2 1 2 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1
17 | 0 17 8 5 3 3 2 2 1 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 1
18 | 0 18 9 6 4 3 3 2 1 2 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1
19 | 0 19 9 6 4 3 2 2 1 1 1 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
20 | 0 20 10 6 5 4 2 2 1 1 2 1 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
21 | 0 21 10 7 5 3 3 3 2 1 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
22 | 0 22 11 7 5 4 3 2 2 2 1 2 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
23 | 0 23 11 7 5 4 3 2 2 2 1 1 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
24 | 0 24 12 8 6 4 4 3 3 2 2 1 2 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
25 | 0 25 12 8 6 5 3 3 2 2 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
26 | 0 26 13 8 6 4 4 3 2 2 2 1 1 2 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0
27 | 0 27 13 9 6 5 4 3 3 3 2 2 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0
28 | 0 28 14 9 7 5 4 4 3 2 2 2 1 1 2 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0
29 | 0 29 14 9 7 5 4 3 3 2 2 2 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0
30 | 0 30 15 10 7 6 5 4 3 2 3 2 1 1 1 2 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1
可以发现0的分界线在黄金分割比附近,直接算一个边界就可以了。边界不好看清的话可以上下拖动滚动条。(我大概有病orz,不过真的能看见,希望大家试试)
代码
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 #include<iostream> 6 #include<map> 7 #include<ctime> 8 using namespace std; 9 const int maxn=1<<13; 10 int n; 11 int main(){ 12 long long x,y; 13 while(~scanf("%lld%lld",&x,&y)){ 14 if(x==0&&y==0)break; 15 if(x>y)swap(x,y); 16 long long w=((double)x*2.0/(sqrt(5.0)-1.0)); 17 if(y<=w&&y!=x)printf("Ollie wins\n"); 18 else printf("Stan wins\n"); 19 } 20 return 0; 21 }
1.根据规则进行推理,在日常写题还是很推荐这种写法的,毕竟博弈论能找到规律的毕竟只是一部分,大部分不用dp的博弈论都是相应对策或者必胜选择的推理(个人感受不一定对)。
记每次一个人开始操作前的两数大的为y,小的x。
全程两人都没有选择(每次的情况都满足y减去一次x就比x小)的时候结果是一定的,那么直接循环找出最终胜利者就可以了(显然满足这种条件的时候循环次数不会太多)。
假如其中有一个人有选择(y可以减n次小的才比x小,n>1)的时候,这个人如果在y中取n个x时必输,这个人就可以在y中取n-1个x,那么对方就必输。
于是有了一个循环解决的写法。
代码
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 #include<iostream> 6 #include<map> 7 #include<ctime> 8 using namespace std; 9 const int maxn=1<<13; 10 int n; 11 int main(){ 12 long long x,y; 13 while(~scanf("%lld%lld",&x,&y)){ 14 if(x==0&&y==0)break; 15 if(x>y)swap(x,y); 16 int w=0; 17 while(x!=0){ 18 if(y%x==0||y-x>x)break; 19 y-=x; 20 w^=1; 21 if(x>y)swap(x,y); 22 } 23 if(w)printf("Ollie wins\n"); 24 else printf("Stan wins\n"); 25 } 26 return 0; 27 }