Description
Input
Output
Stan wins.
or
Ollie wins.
assuming that both of them play perfectly.
Sample Input
162 17 34012226
Sample Output
Stan wins. Ollie wins. Stan wins.
题意:
每局游戏给出一个数字n,让p从1开始让两人轮流乘上一个2~9的整数,谁使得p最先大于等于p则胜利。
分析:其实这个题目得出答案很容易,枚举必胜区间即可:
显然,假如n为 [ 2, 9 ] 的时候,先手肯定赢。如果n为[10,18]的时候,后手肯定赢。
这个时候我们可以猜想,[ 19, X ] 一定是个先手必胜区间。X是多少呢?
我们先定义一个运算,Up(x) 表示对x进行向上取整,/ 表示浮点数除法意思就是带小数。
好我们回到刚才那个问题:因为第一次乘数的是先手,那么假如第一次乘的数为M ,则 Up(X/M) 一定处于后手必胜区。要想X最大,那么Up(X/M) = 18.所以M = 9,X = 18*9,所以当n为[ 19, 18 * 9 ] 时。先手一定可以乘上某个数使得Up(X/M)属于[ 10 , 18 ] .
这个时候我们可以看到规律。
[ 2 , 9 ] 先手
[ 10 , 18 ] 后手
[ 19 , 18 * 9 ] 先手
[ 18 * 9 + 1, 18 * 18 ] 后手
[ 18 * 18 + 1 , 18 * 18 * 9 ] 先手
[ 18 * 18 * 9 + 1, 18 * 18 * 18 ] 后手
所以对n一直除以18 直到小于等于18。如果n小于等于9就是先手,否则就是后手。
嗯答案得到了。如果我们想知道策略呢?
其实很简单
我们把先手区间定义为必胜区间,后手定义为必败区间。
如果我方是必胜方,每次我方乘数的时候,只需要使得自己乘数后的p 使得 Up(n/p)处于必败区间。就可以了。
其实这个问题可以和巴什博弈联系起来。巴什博弈是在一堆石子中,最多可以拿m个最少可以拿n个(n<=m),问先手还是后手赢。其实就是把加变为了乘。
解决的策略其实也是很相似的。
代码:
#include <iostream>
using namespace std;
int main(){
int n;
while(cin>>n){
while(n > 18){
n = n/18;
}
if(n <= 9)
cout << "Stan wins." << endl;
else
cout << "Ollie wins." << endl;
}
return 0;
}