博弈论基础

先引入几个概念:
公平组合游戏(IGC):
1)有两个玩家,游戏规则对两个玩家公平
(2)游戏状态有限,能走的步数也是有限的
(3)轮流走,当一个玩家不能走时游戏结束
(4)游戏的局势不能区分玩家的身份,例如黑白棋就是不行的
特征:
给定初始局势,指定先手玩家,如果双方都采取最优策略,那么获胜者已经确定了,也就是说ICG问题存在必胜策略。

先手必胜:
指当轮到自己走时,下一步能够走到必败的局面。
先手必败:
指当轮到自己走时,下一步无论如何都无法走到必败的局面。

Nim游戏

给定 n 堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。

问如果两人都采用最优策略,先手是否必胜。

输入格式
第一行包含整数 n 。

第二行包含 n 个数字,其中第 i 个数字表示第 i 堆石子的数量。

输出格式
如果先手方必胜,则输出 Yes。

否则,输出 No。

数据范围
1≤n≤105 ,
1≤每堆石子数≤109
输入样例:
2
2 3
输出样例:
Yes

博弈论经典案例,大意是有n堆石子,每堆石子若干颗,两个人轮流拿走任意一堆石子中的任意颗数,问先手方是必胜还是必败。
先给结论:将每堆石子的数量异或,如果最后的异或和为零则必败否则必胜。

证明:
第一种情况:0 ^ 0 ^ 0 … ^ 0 = 0.
当每堆石子都是0时,无法走出任何行动,必败。

第二种情况:a1 ^ a2 ^ … ^ an = x ,x != 0.
当x不等于0时,设x二进制的最高位为第k位,至少存在一个ai第k位也为1,且ai ^ x < ai,(因为自k位异或后变0,后面一定会变小,而k位之前不变),所以一定存在操作可以将ai变成ai ^ x,这时异或和为0,所以当第二种情况时,一定可以将当前局面变为异或和为0的情况直至第一种情况出现。进入必败为必胜,所以可证?

其实要证明第二种情况一定成立还需要排除一种情况,那就是当先手行动进入异或和为0时,后手一定无法通过操作使得局面继续保持异或和为0的情况。
这里通过反证法进行证明:假设a1 ^ a2 ^ ai ^ … ^ an = 0,我们通过修改ai为aj使得a1 ^ a2 ^ aj ^ … ^ an = 0.将等式两边异或可得ai ^ aj = 0,ai = aj,与假设矛盾,所以可证的不存在。

有了结论代码就很简单了:

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int n,x,res = 0;
    cin>>n;
    while(n -- )
    {
        cin>>x;
        res ^= x;
    }
    if(res)
        cout<<"Yes";
    else
        cout<<"No";
    return 0;
}

来个变式:台阶-Nim游戏

现在,有一个n级台阶的楼梯,每级台阶上都有若干个石子,其中第i级台阶上有ai个石子(i≥1)。

两位玩家轮流操作,每次操作可以从任意一级台阶上拿若干个石子放到下一级台阶中(不能不拿)。

已经拿到地面上的石子不能再拿,最后无法进行操作的人视为失败。

问如果两人都采用最优策略,先手是否必胜。

输入格式
第一行包含整数n。

第二行包含n个整数,其中第i个整数表示第i级台阶上的石子数ai。

输出格式
如果先手方必胜,则输出“Yes”。

否则,输出“No”。

数据范围
1≤n≤10^5,
1≤ai≤10^9
输入样例:
3
2 1 3
输出样例:
Yes

题意:同样是n堆石子,放在n级台阶上,问先手必胜还是必败

先看偶数台阶上的石子,由于是偶数台阶,先手拿取的石子一定会放在奇数台阶上,而后手只要将先手拿取放在奇数台阶的石子放到再下一级,奇数台阶的下一级有两种情况:偶数台阶或者地面,所以只需要后手一直重复先手的动作,一定存在先手面临偶数台阶无石子可操作的局面,所以不考虑奇数台阶的情况,无论偶数台阶石子有多少,先手必败,并且偶数台阶的石子不影响最终结果。

再来看奇数台阶的情况,奇数台阶的下一级为偶数台阶或者地面,由于偶数台阶不影响最终结果,所以可以转化为奇数台阶下一级全为地面,每次将奇数台阶上的若干石子放到地面,就转化为了Nim游戏的模型,所以只需奇数台阶上的石子数异或和为0即为必败局面,否则为必胜。

代码:

#include<bits/stdc++.h>
using namespace std;

int flag = 1,n,x,res;

int main()
{
    cin>>n;
    while(n -- )
    {
        cin>>x;
        if(flag % 2)
        res ^= x;
        flag ++;
    }
    if(res)
    cout<<"Yes";
    else
    cout<<"No";
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值