信息学奥赛一本通 1218:取石子游戏 | OpenJudge NOI 2.5 6266:取石子游戏

【题目链接】

ybt 1218:取石子游戏
OpenJudge NOI 2.5 6266:取石子游戏

【题目考点】

1. 博弈:完全信息博弈

博弈树:
博弈树的结点对应于某一个棋局,其分支表示走一步棋;根部对应于开始位置,其叶表示对弈到此结束。在叶节点对应的棋局中,竞赛的结果可以是赢、输或者和局 。
如果该博弈为完全信息博弈,那么博弈双方都清楚整个博弈树,那么该游戏在开始前即可确定先手必胜/必败/或最好情况只能是平局。
博弈树的每一个结点有三种标识:w(对应于赢)、d(对应于和局)或者l(对应于输)。
如果当前的棋局是标有w的,那么此时先手有必胜策略。如果结点标识为d,那么除非对手失误,否则先手最好的情况也只能达成和局;如果结点标识为l ,那么无论先手如何下,对手都有必胜策略。
确定各结点标识的方法如下:
首先,叶子结点标识棋局结束,可以很多容易地判定叶子结点的标识。
如果该结点的子结点都是w,那么说明自己走一步棋后,对手总有必胜策略,那本结点对应的棋局先手必输,标记为l。
如果该结点的子结点存在l,那么自己走一步棋后,棋局变为标记为l的结点,对手输,自己胜。所以本结点标记为w。
如果该结点的子结点不存在l,但存在d,那么自己走一步棋后,棋局变为标记为d的结点,可以争取到平局,本结点标记为d。
按此方法标记每个结点。
如果根结点标记为w,那么先手有必胜策略。如果根结点标记为l,那么后手有必胜策略。如果根结点标记为d,那么先手有至少达到和局的策略。

【解题思路】

1. 建立博弈树

两堆石子数目确定后,构成一种棋局,在双方都不犯错误的情况下,先手必胜或必败。
根据题意,画出测试用例对应的博弈树:
信息学奥赛一本通 1218:取石子游戏 | OpenJudge NOI 2.5 6266:取石子游戏_第1张图片

先画出剩下两堆石子个数可能的变化,再确定标记。
在(2,10)时,此时先手可以做到在10中拿2的5倍,将第2堆拿完,取得胜利,所以此处标记为w。(2,10)的父结点(10,12)自然为l。而(10,12)的父结点自然为w。
对于输入(15,24),可以得到博弈树:
信息学奥赛一本通 1218:取石子游戏 | OpenJudge NOI 2.5 6266:取石子游戏_第2张图片
进而得知先手必输。

2. 证明题目中的提示

证明:如果 ⌊ a b ⌋ < 2 \lfloor \frac{a}{b} \rfloor < 2 ba<2,那么先手只有唯一的一种取法。

此时a < 2b,唯一的一种取法为:在a中取走b,剩下的两堆石子为(a-b, b),而且a-b < b。

证明:假设石子数目为(a,b)且 a ≥ b a \ge b ab,如果 ⌊ a b ⌋ ≥ 2 \lfloor \frac{a}{b} \rfloor \ge 2 ba2则先手必胜。

证明:因为 ⌊ a b ⌋ ≥ 2 \lfloor \frac{a}{b} \rfloor \ge 2 ba2,记 x = ⌊ a b ⌋ x=\lfloor \frac{a}{b} \rfloor x=ba,那么一定存在 x ≥ 1 x \ge 1 x1,使得 a b − 1 < x ≤ a b ⇒ 0 ≤ a − x b < b \frac{a}{b}-1< x \le \frac{a}{b}\Rightarrow 0\le a-xb< b ba1<xba0axb<b,因此也有: b ≤ a − ( x − 1 ) b < 2 b b\le a-(x-1)b<2b ba(x1)b<2b
因此至少存在以下两种取石子方案:

  • 方案1:a中取走xb,剩下两堆石子为:(a-xb, b)
  • 方案2:a中取走(x-1)b,剩下两堆石子为:(a-(x-1)b, b)
  1. 如果情况(a-xb, b)的标记为w(即先手必胜),那么本轮本方采用方案2,对手遇到的情况为(a-(x-1)b, b),而 b ≤ a − ( x − 1 ) b < 2 b b\le a-(x-1)b<2b ba(x1)b<2b,亦即 ⌊ a − ( x − 1 ) b b ⌋ < 2 \lfloor\frac{a-(x-1)b}{b} \rfloor < 2 ba(x1)b<2,根据第1点的证明可知此时对手只有一种取法,也就是在a-(x-1)b中取走b,得到两堆石子为(a-xb, b),本方面对的情况为(a-xb, b)是w标记的情况,本方先手必胜。
  2. 如果(a-xb, b)的标记为l(即先手必输),那么本轮本方采用方案1,得到(a-xb,b),对手面对(a-xb,b)情况必败,本方必胜。

综上,在 ⌊ a b ⌋ ≥ 2 \lfloor \frac{a}{b} \rfloor \ge 2 ba2时先手必胜。

3. 编码

设递归函数bool dfs(int a, int b),意为当两堆石子分别是(a,b),且 a ≥ b a\ge b ab,时先手是否必胜。

  • 如果当前两堆石子a是b的倍数,那么先手可以直接拿光a,本轮先手必胜。
  • ⌊ a b ⌋ ≥ 2 \lfloor \frac{a}{b} \rfloor \ge 2 ba2即整除运算a/b >= 2,那么本轮先手必胜。
  • 否则本轮只能有一种取法,就是在a中取走b,剩下两堆石子为(b, a-b),其中 b ≥ a − b b \ge a-b bab。下一轮先手必胜的情况为dfs(b, a-b),如果下一轮先手必胜,那么本轮先手必败。如果下一轮先手必败,那么本轮先手必胜。所以下一轮的先手必胜情况与本轮相反,所以本轮的先手必胜情况为!dfs(b, a-b)

【题解代码】

解法1:博弈 递归
#include 
using namespace std;
bool dfs(int a, int b)//a:较多一堆石子的数量 b:较少一堆石子的数量  返回:此时是否先手必胜 
{
    if(a%b == 0)
        return true;//一堆是另一堆的整数倍,此时先手一定可以拿光一堆获得胜利。
    if(a/b >= 2)//如果floor(a/b)>=2,那么先手必胜 
        return true;
    else
        return !dfs(b, a-b);//下一轮先手是否必胜的情况为dfs(b, a-b),本轮先手的获胜情况与之相反。 
}
int main()
{
    int a, b;
    while(cin >> a >> b)
    {
        if(a == 0 && b == 0)
            break;
        if(a < b)
            swap(a, b);//保证a较大,b较小 
        cout << (dfs(a, b) ? "win" : "lose") << endl;
    }
    return 0;
}

你可能感兴趣的:(信息学奥赛一本通题解,OpenJudge,NOI题解,c++)