博弈论做题总结1

博弈论做题总结1

  • Calendar Game(日期)
      • 题目大意
      • 分析
      • 代码
  • Euclid's Game(两数关系)
      • 题目大意
      • 分析
      • 代码
  • Good Luck in CET-4 Everybody!(巴什)
      • 问题描述
      • 分析
      • 代码

关于博弈论的基本内容详见本人这篇博客博客链接
这里以例题,补充一些博弈论的其他知识和做题的注意点。

Calendar Game(日期)

题目大意

两个人玩一种游戏,游戏内容为:在1900年1月1日到2001年11月4日之间的某一个日期开始,分别往下报日期,有两种报法,可以报该天的下一天,也可以报下个月的这一天(如果下个月没有这一天则不可以这样报),最后报到2001年11月4日的人赢。问对于给定的日期,先手是赢是输。(两人均采用最优策略)

分析

  1. 一个博弈论的相关定理:
    我们假设 所有终结点 是 必败点P。
    无论如何操作,必败点P 都只能进入 必胜点 N。(不管怎么做,都会使对手处于必胜状态)
    从任何必胜点N 操作,至少有一种方式可以进入必败点 P。(当前状态 采取 最优策略 可以使对手 处于必败状态)
    假设我站在先手的状态:
    (注意: a -> b, 如果b全部是必胜态, 则a是必败态, 如果存在一个b是必败态,则a是必胜态。)
  2. 通常我们分析必胜点和必败点都是以终结点进行逆序分析。

看到该题,我们不妨直接打表试图寻找规律。

必败点 必胜点
11.4 11.3
11.2 11.1
10.31 10.30
10.29 10.28
10.1 9.30(直接转移到10.1)
9.28 9.29(直接转移到10.29)

解释9.28点:(1)可以一下跳到10.28,使对手处于必胜状态,(2)可以跳到9.29,对手也处于必胜状态。所以9.28为必败点。
9.29点为必胜点:存在该人报 10.29使对手处于必败状态。

必败点 必胜点
9.2 9.1
8.31 8.30(直接跳到8.31,使对手必败)
8.29 8.28
8.3 8.2
8.1 7.31
7.30 7.29
7.2 7.1
6.30(跳到7.30)
6.29 6.28
6.3 6.2
6.1 5.31
5.30 5.29
5.2 5.1
. 4.30(直接跳到5.30)
4.29 4.28
4.5 4.4
4.3 4.2
4.1 3.31
3.30 3.29
3.2 3.1
2.29 2.28
2.3 2.2
2.1 1.31
1.30 1.29
1.2 1.1
12.31 12.30
12.29 12.28
12.1 11.30(跳到12.1)
. 11.29(跳到12.29)
11.28 11.27

发现:

  1. 2.28是一个必胜点,2.29是一个必败点,这俩是固定的
  2. 不管是月份加一,还是日期加一,都改变了奇偶性(必胜点 月份加日期 = 偶数, 必败点两者相加为 奇数)只有两个点不同:9.30, 11.30为必胜点 例外

代码

#include 
#include 
using namespace std;
int main(void){
    int t;
    cin >> t;
    while(t --){
        int n, y, r;
        scanf("%d%d%d", &n, &y, &r);
        if((y + r) % 2 == 0 ) puts("YES");
        else if( r == 30 && (y == 11 || y == 9)) puts("YES");
        else puts("NO");
    }
    return 0;

}

Euclid’s Game(两数关系)

题目大意

小A和小B,准备玩一个游戏,玩法是这样的,从两个自然数开始比赛。第一个玩家小A从两个数字中的较大者减去两个数字中较小者的任何正倍数,前提是得到的数字必须是非负的。然后,第二个玩家小B对得到的两个数字做同样的处理,两个玩家交替进行,直到一个玩家能够从大的数字中减去较小数字的倍数,达到0,从而获胜

分析

我们涉及到两个数,盲举自然是不太可行的。可以从两数大小关系入手
我们规定 a >= b.(不符合时,交换即可)

  1. 如果 a = b 或者 a % b = 0这两种情况对于先手都是必胜态。
  2. 当 a >= 2b, 对于先手来说, 一定可以得到 a % b 的值,
    如果 b % (a % b) = 0,后手必胜。
    先手可以把两数变成 a % b + b 和 b, 后手 只能将两数变成 a % b 和 b。先手再次处于必胜状态。
  3. b < a < 2 * b,进行模拟,直到出现必胜态,通过记录操作次数(奇偶)来判断谁赢。

代码

#include 
#include 
using namespace std;
int a, b;
int main(void){
    while(1){
        scanf("%d%d", &a, &b);
        if(a == 0 && b == 0) break;
        if(a < b) swap(a, b);
        int cnt = 0;
        while(b){
            if(a % b == 0 || a >= 2 * b){
                cnt ++;
                break;
            }
            a -= b;
            swap(a, b);
            cnt ++;
        }
        if(cnt % 2) puts("Stan wins");
        else puts("Ollie wins");

    }
    return 0;
}

Good Luck in CET-4 Everybody!(巴什)

问题描述

Kiki和Cici打牌的时候可没忘记专业,她们打牌的规则是这样的:
1、 总共n张牌;
2、 双方轮流抓牌;
3、 每人每次抓牌的个数只能是2的幂次(即:1,2,4,8,16…)
4、 抓完牌,胜负结果也出来了:最后抓完牌的人为胜者;
假设Kiki和Cici都是足够聪明(其实不用假设,哪有不聪明的学生~),并且每次都是Kiki先抓牌,请问谁能赢呢?

分析

和巴什博弈很像,小编啊,,真的是,, 看到这题解都震惊了,,害,希望这道题能触碰到大脑里的某条神经, 从而变得聪明点。。

任何正整数都能写成若干个2的整数次方幂之和
由于规定只能取2的某个整数此方幂,只要你留给对手的牌数为 3 的倍数(3 * n)时,那你就必赢。对于每个 3 只要他取出1个(或2个)那你就 取出2个(或1个),比如剩下 8 个, 对手取出5个, 5 = 3 + 2, 那你就取出 0 + 1个, 剩下三个,必赢

代码

#include 
#include 
using namespace std;
int main(void){
    int n;
    while(scanf("%d", &n) != EOF){
        if(n % 3) puts("Kiki");
        else puts("Cici");
    }
    return 0;
}

你可能感兴趣的:(笔记)