题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1517
题意:从p=1开始Stan和Ollie各取一个2到9之间的数乘以p直到最先到达p>=n的胜利。
1、找规律:
①、如果输入是2~9,因为Stan是先手,所以Stan必胜。
②、如果输入是10~18(9*2),因为Ollie是后手,不管第一次Stan乘的是多少,Stan肯定在2~9之间,如果Stan乘以2,那么Ollie就乘以9,那么Ollie乘以大于1的数都能超过10~18中的任何一个数,Ollie必胜。
③、如果输入的是19~162(9*2*9),那么这个范围Stan必胜。
④、如果输入是163~324(9*2*9*2),这个是Ollie的必胜范围。
…………
这样一种状态只要除以18就可以转化为上一个同种状态。因为我们只需判断范围就可以了,所以直接用浮点数即可达到效果。
所以一直除到状态①和状态②然后判断一下就可以了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
int main() {
double n;
while(~scanf("%lf", &n)) {
while(n > 18) n = n / 18;
if(n > 9) puts("Ollie wins.");
else puts("Stan wins.");
}
return 0;
}
2、分析必胜态和必败态
由于每次都是从p=1开始的,所以只要判断每个游戏中1为必败点还是必胜点即可。
依照上面所提到的算法,将终结位置,即[n,无穷]标记为必败点;
然后将所有一步能到达此必败段的点标记为必胜点,即[n/9,n-1]为必胜点;
然后将只能到达必胜点的点标记为必败点,即[n/9/2,n/9-1]为必败点;
重复上面2个步骤,直至可以确定1是必胜点还是必败点。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
int main() {
ll n;
while(~scanf("%I64d", &n)) {
int turn = 1; //必胜态和必败态是交替进行的
while(n > 1) {
if(turn&1) {
if(n % 9) n = n / 9 + 1; //除不尽要进行上取整
else n = n / 9;
}
else {
if(n % 2) n = n / 2 + 1;
else n = n / 2;
}
turn++;
}
if(turn&1) puts("Ollie wins.");
else puts("Stan wins.");
}
return 0;
}