题目描述
传送门
这道题真正的题意是:起始数为1,每次可以将这个数乘2−9中的任意一个数,首先得到>=n的数的人获胜。问是否有必胜策略
而Po读错题了写完了代码竟然A了!并且刚刚发现!
但是Po并没有删除这篇题解就是因为Po不忍心删除花了好久证明的做法→_→,并且Po觉得如果是下面这个错的题意的话这个做法应该是正确的!
一笑而过...
题意:起始数为1,每次可以将这个数乘2或乘9,首先得到>=n的数的人获胜。问是否有必胜策略。
题解
这道题我用了一个非常奇怪的方法。
当n<=18的时候随便讨论一下就行了。来看n>18的情况。
首先我们将n分解为
18k∗x
的形式(x有可能为实数)。
1、当x=1的时候,先手必败。因为无论先手选择2还是9,后手都可以选择另一个数将其凑成18.
2、当x>9的时候,先手必败。同样无论先手选择2还是9,后手都可以选择另一个数将其凑成18。而最后凑不成整18的那个数,先手无论选2还是选9都会帮助后手取得胜利。
3、当x<=2的时候,先手必胜。因为先手可以先选一个2,然后无论后手怎么选,都选另一个数来凑成18。
4、当
2<x<=9
的时候,是最麻烦的一种情况。后手不会傻到每一次都给先手去凑18,所以一定是先手先选择一个数,然后根据后手的选择来凑18,但是先手唯一担心的就是这个过程中自己还没有凑到18后手就用一个数和先手选择的第一个数组合然后超过n了。我们可以发现9是2的4.5倍。当
2<x<=4.5
的时候,如果先手先选择了2,那么后手只需要每次都选择2就可以保证即使先手选9也拿不到n,在这个过程中如果先手选择了2那么权利反转,同样是先手必败;如果先手先选择了9,那么后手只需要也选择9,然后根据先手的选择凑18,就可以保证最后的胜利。当
4.5<x<=9
的时候,先手只需要先选择9,然后根据后手的选择凑18,这样无论如何后手怎样选都无法在没凑到18的情况下达到n,此时先手必胜。
代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
double n;
const double mi=18.0;
int main()
{
while (~scanf("%lf",&n))
{
if (n<mi)
{
if (n<=9) puts("Stan wins.");
else puts("Ollie wins.");
continue;
}
double p;
for (p=1.0;;)
if (p*mi>n) break;
else p*=mi;
double t=n/p;
if (t==1) puts("Ollie wins.");
else if (t>9) puts("Ollie wins.");
else if (t<=2) puts("Stan wins.");
else if (t<=4.5) puts("Ollie wins.");
else puts("Stan wins.");
}
}