理解不了大佬们怎么推出来的,我根据一个大佬写的整理一下
,统一一下标准,设输入为N,一个回合指的是一个人拿一次物品的机会,a的上个回合也是a的操作,不是b
设当前状态获胜,且a为获胜者,b为必败者
a上个回合拿了之后数量变为 M M M,但是 b 不能获胜,因此
M ∗ 9 < N M*9<N M∗9<N
以为a能够必胜,
但是 b 肯定不想让当前人获胜,则 b 拿的石子只会是 2 2 2 倍,设当前人拿的为 p p p 倍,则:
M ∗ 2 ∗ p > = N M*2*p>=N M∗2∗p>=N
我要得到M的取值范围,由 1 1 1 式可以得到 M M M的右端点, 2 2 2 式可以得到左端点,
肯定要取到极限值,则 p p p 要取最大值 p = 9 p=9 p=9
然后就实现了状态转移
N = M m i n = ⌈ N 18 ⌉ N=M_{min}=\left \lceil \frac{N}{18} \right \rceil N=Mmin=⌈18N⌉
若看懂上面的过程,就不需要看这段话了
ICG博弈的定义,对于第 i 个状态只有先手必胜和先手必败两种可能,先手必败记为P-position,先手必胜记为 N-position
- 无法移动的局面记为 P-position
- 可以移动到 N-position 局面的只有 P-position
- P-position 只能移动到 N-position
懂了这三个定义之后重新在分析一遍该题,最后结束游戏的人一定是 N-position,能到达他的只有 P-position,推理一下,有能力到达 P-position 又一定是 N-position,因为最后一个人获胜,设获胜的那个人在前一轮回合初始的石子数为 M 个,他拿的石子数为 x ,又因为M 是 P-position 局面,P-position 局面只能移动到 N-position ,则 对于任意 x 有 , M ∗ x < N 且 M ∗ x ∗ 9 > = N x有,M*x<N 且 M*x*9>=N x有,M∗x<N且M∗x∗9>=N
因为对于任何一个x都要满足上述条件,则对 M取交集,可知 N 18 < = M < N 9 \frac{N}{18}<=M<\frac{N}{9} 18N<=M<9N
M是b回合的初始状态,也是a上一轮游戏拿的数量的可选区间
回归一下题意,题意要我们拿的数量>=N则获胜,现在变为拿的数量属于M也获胜,能不能直接转化为拿的数量 > = M m i n >=M_{min} >=Mmin获胜呢?
设 x < M m i n 并 且 x ∗ 9 > = M m i n x<M_{min} 并且 x*9>=M_{min} x<Mmin并且x∗9>=Mmin
若存在 p ∈ [ 2 , 9 ] p\in[2,9] p∈[2,9],使得 x ∗ p ∈ M x*p\in M x∗p∈M,则上推论成
很容易推出 x ∈ [ N 18 ∗ 9 , N 18 − 1 ] x\in [\frac{N}{18*9},\frac{N}{18}-1] x∈[18∗9N,18N−1]
Ep 使得 x ∗ p ∈ M x*p\in M x∗p∈M <==> Ep 使得 x ∈ [ N 18 p , N 9 p ] x\in [\frac{N}{18p},\frac{N}{9p}] x∈[18pN,9pN]
设 G = [ N 18 p , N 9 p ] , p ∈ [ 2 , 9 ] G=[\frac{N}{18p},\frac{N}{9p}] ,p\in[2,9] G=[18pN,9pN],p∈[2,9]取并集,得 G = [ N 18 ∗ 9 , N 18 ] G=[\frac{N}{18*9},\frac{N}{18}] G=[18∗9N,18N]
x是G的子集,这意味着上面那个推论是正确的.
这样的话,无论 dfs,还是打表都方便很多,我直接用 dfs 写吧,因为它好理解
#include <stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<vector>
#include<iostream>
#include<cstring>
#include<set>
#include<cmath>
#include<queue>
#define ll long long
const int mod=1e9+7;
using namespace std;
const int N=1e3+6;
const ll inf=1e18;
bool dfs(int n){
if(n<=9)return 1; //先手拿的最大值为9
if(n<=18)return 0; //后手拿的最大值为2*9
return dfs((n+17)/18); //向上取整
}
int main() {
int n;
while(scanf("%d",&n)!=EOF)
{
printf("%s\n",dfs(n)?"Stan wins.":"Ollie wins.");
}
}