[POJ2505] A multiplication game

理解不了大佬们怎么推出来的,我根据一个大佬写的整理一下
,统一一下标准,设输入为N,一个回合指的是一个人拿一次物品的机会,a的上个回合也是a的操作,不是b
设当前状态获胜,且a为获胜者,b为必败者
a上个回合拿了之后数量变为 M M M,但是 b 不能获胜,因此

M ∗ 9 &lt; N M*9&lt;N M9<N

以为a能够必胜,
但是 b 肯定不想让当前人获胜,则 b 拿的石子只会是 2 2 2 倍,设当前人拿的为 p p p 倍,则:

M ∗ 2 ∗ p &gt; = N M*2*p&gt;=N M2p>=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

  1. 无法移动的局面记为 P-position
  2. 可以移动到 N-position 局面的只有 P-position
  3. P-position 只能移动到 N-position

懂了这三个定义之后重新在分析一遍该题,最后结束游戏的人一定是 N-position,能到达他的只有 P-position,推理一下,有能力到达 P-position 又一定是 N-position,因为最后一个人获胜,设获胜的那个人在前一轮回合初始的石子数为 M 个,他拿的石子数为 x ,又因为M 是 P-position 局面,P-position 局面只能移动到 N-position ,则 对于任意 x 有 , M ∗ x &lt; N 且 M ∗ x ∗ 9 &gt; = N x有,M*x&lt;N 且 M*x*9&gt;=N xMx<NMx9>=N
因为对于任何一个x都要满足上述条件,则对 M取交集,可知 N 18 &lt; = M &lt; N 9 \frac{N}{18}&lt;=M&lt;\frac{N}{9} 18N<=M<9N
M是b回合的初始状态,也是a上一轮游戏拿的数量的可选区间
回归一下题意,题意要我们拿的数量>=N则获胜,现在变为拿的数量属于M也获胜,能不能直接转化为拿的数量 &gt; = M m i n &gt;=M_{min} >=Mmin获胜呢?
x &lt; M m i n 并 且 x ∗ 9 &gt; = M m i n x&lt;M_{min} 并且 x*9&gt;=M_{min} x<Mminx9>=Mmin
若存在 p ∈ [ 2 , 9 ] p\in[2,9] p[2,9],使得 x ∗ p ∈ M x*p\in M xpM,则上推论成
很容易推出 x ∈ [ N 18 ∗ 9 , N 18 − 1 ] x\in [\frac{N}{18*9},\frac{N}{18}-1] x[189N,18N1]
Ep 使得 x ∗ p ∈ M x*p\in M xpM <==> 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=[189N,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.");
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值