博弈论做题总结2

邂逅明下(巴什)

题目大意

n 枚硬币, 每次至少取p枚,最多q枚,如果剩下少于p枚就要一次取完。求先取的人是否有必胜策略。

分析

【小编当时遇题的思考】自然是从n和两数的关系入手, n <= p时先手必输, p < n <= 2p时,先手可赢。但当 n > 2p时,我们就得讨论q的大小关系,脑子一团乱麻,直接搜答案了(对自己的菜很是无语。。倒也不为难自己)
【合理解释】
感觉巴什博弈就是两者取的个数拼凑成一个数(一般固定),剩下一个不得不取的数就是必胜必败的情况
我们不妨换个比较的思路, n 和 (p + q)的关系。

  1. n = (p + q) * r, 先手第一次取q个, 后手每取k个,先手变取(p + q - k)个, 这两个均属于 [p, q]的范围。最后必胜 p个留给后手,先手必胜
  2. 若 n = (p + q) * r + left 个,
    (1) 1 < left <= p ,先手必败。先手每取k个,后手均取(q + p - k)个,最后留 left给先手,先手必败
    (2) left 在[p + 1, p + q - 1] 之间, 则先手可以拿走 t 个(拿的数量看 left 的大小,不能>=left, 保证最后剩的为[1, p]个), 以后后手每取k个,先手便取(p + q - k) 个, 最后剩 [1, p]个是后手要拿的,先手胜。

另一种思考方式可能更好:
此题为巴什博弈得变形,n % (p + q)的值与p比较, 若 = 0 或 > p, 则先手必胜(因为他可以不是最后拿的那个), 若 0<值<= p 则先手必输。

#include <algorithm>
#include <iostream>
using namespace std;
int main(void){
    int n, p, q;
    while(scanf("%d%d%d", &n, &p, &q) != EOF){
        if(n % (p + q) > p || n % (p + q) == 0) puts("WIN");
        else puts("LOST");
    }
    return 0;
}

Nim or not Nim? (尼姆博奕变形)

题目大意

尼姆博弈游戏在原有的游戏规则上附加一条——玩家除了取石子操作外,还可以将一堆石子分成两份,这也算一次操作;

思路

求出现的每堆石子的SG值再进行异或运算;我们重要的是求出每堆石子的SG值。打表也需要一定的基本思想
SG[x]=mex(SG[y]). y是x的后续(通俗的讲,y是x可以一步到达的状态));mex()函数是求集合中未出现的最小非负整数如:mex(0,2,3)=1; 更多内容可参考小编的这篇博客
博客链接
SG(0) = 0;
SG(1) = mex(SG(0)) = 1;
SG(2) = mex(SG(0), SG(1), SG(1) ^ SG(1)) = 2;
2 可以分成两堆 1 , 1,
【一个定理:每一堆都可变成多个局面,每个局面都有两堆b1, b2, sg(b1, b2) = sg(b1) ^ sg(b2)】
SG(3) = mex(SG(0), SG(1), SG(2), SG(1) ^ SG(2)) = mex(0, 1, 2, 3) = 4;
SG(4) = mex(SG(0), SG(1), SG(2), SG(3), SG(1) ^ SG(3) , SG(2) ^ SG(2)) = mex(0, 1, 2, 4, 2, 0) = 3;
SG(5) = mex(SG(0), SG(1), SG(2), SG(3), SG(4), SG(1) ^ SG(4), SG(2) ^ SG(3)) = mex(0, 1, 2, 4, 3, 2, 1) = 5
SG(6) = mex(SG(0), SG(1), SG(2), SG(3), SG(4), SG(5), SG(1) ^ SG(5), SG(2) ^ SG(4), SG(3) ^ SG(3)) = mex(0, 1, 2, 4, 3, 5, 4, 1, 0) = 6
SG(7) = mex(SG(0), SG(1), SG(2), SG(3), SG(4), SG(5), SG(6), SG(1) ^ SG(6), SG(2) ^ SG(5), SG(3) ^ SG(4)) = 8

SG(8) = mex(SG(0), SG(1), SG(2), SG(3), SG(4), SG(5), SG(6), SG(7), SG(1) ^ SG(7), SG(2) ^ SG(6), SG(3) ^ SG(5), SG(4) ^ SG(4)) = 7

这样觉得偶然的话,那就再打几个。

规律:
n % 4 = 0时, SG(n) = n - 1;
n % 4 = 3: SG(n) = n + 1;
其他: SG(n) = n。

代码

#include <algorithm>
#include <iostream>
using namespace std;
int n, x;
int main(void){
    int t;
    cin >> t;
    while(t --){
        scanf("%d", &n);
        int res = 0;
        int t = 0;
        for(int i = 0; i < n; i ++){
            scanf("%d", &x);
            if(x % 4 == 0) res ^= (x - 1);
            else if(x % 4 == 3) res ^= (x + 1);
            else res ^= x;
        }
        if(res == 0) puts("Bob");
        else puts("Alice");
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xuhx&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值