博弈问题总结

目录

1、尼姆游戏 Nim Game

2、巴什博弈 Bash Game

3、威佐夫博弈 Wythoff Game

4、斐波那契博弈 Fibonacci Game

5、博弈问题练习:


1、尼姆游戏 Nim Game

给定 N 堆物品,第 i 堆物品有 Ai个。两名玩家轮流行动,每次可以任选一堆,取走任意多个物品,可把一堆取光,但不能不取。取走最后一件物品者获胜。两人都采取最优策略,问先手是否必胜。

理解:

如果n堆物品的数量异或等于0,说明起码两两配对。如果是两两配对的情况,那么后手可以跟着先手的步骤走。

  • 比如 先手拿一堆,那后手就拿数量相同的那一堆;

  • 如果先手拿某堆的其中1个,那后手就跟着拿与之配对的那一堆的1个;

其他的情况类型,这样可推得最后一个肯定是由后手拿的,所以,如果异或等0,先手必败!

#include <cstdio>
using namespace std;
int n, x, ans;
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i) {
        scanf("%d", &x);
        ans ^= x;
    }
    if(ans == 0) puts("No");
    else puts("Yes");
    return 0;
}

2、巴什博弈 Bash Game

有 1 堆石子,总个数是 n ,两名玩家轮流在石子堆中拿石子,每次至少取 1 个,至多取 m 个。取走最后一个石子的玩家为胜者。判定先手和后手谁胜。

结论:

如果 n%(m+1) ==0 ,先手必败

证明:

情况1:当 n ≤ m 时,显然先手获胜 此时 n%(m+1) != 0

情况二: 当 n = m + 1 时,先手最多可取走 m 个,无论其取走多少个,剩下的后手总能一次取完。

情况三: 若 ( m + 1 ) ∣ n ,假设先手拿走了 x 个,那么后手一定可以拿走 ( m + 1 ) − x 个,这样无论怎么拿剩下的石头个数都将是 m + 1 的倍数。那么最后一次取的时候石头个数必定还剩下 m + 1 个,即情况二。

int main() {
	scanf("%d%d", &n, &m);
    if (n % (m + 1))puts("first win");
    else puts("second win");
    return 0;
}

3、威佐夫博弈 Wythoff Game

有两堆石子,石子数可以不同。两人轮流取石子,每次可以在一堆中取,或者从两堆中取走相同个数的石子,数量不限,取走最后一个石头的人获胜。判定先手是否必胜。

黄金分割数

(1.0+sqrt(5.0))/2.0 = 1.618

int a,b;scanf("%d %d",&a,&b);
if(a>b)swap(a,b);
int ans = (b-a)*((1.0+sqrt(5))/2.0);
if(ans==a)puts("0\n"); // 先手必败
else puts("1\n");

4、斐波那契博弈 Fibonacci Game

有一堆个数为n的石头,双方轮流取石头。规则如下:

  • 先手不能第一次就全部取完,且至少取1颗。

  • 之后每次最少取1颗,至多为对手刚取石头数的2倍。

结论:

石头数为斐波那契数,则先手必败

5、博弈问题练习:

取球问题 

今盒子里有n个小球,A、B两人轮流从盒中取球,两人都很聪明,不会做出错误的判断。我们约定:每个人从盒子中取出的球的数目必须是:1,3,7或者8个;轮到某一方取球时不能弃权;A先取球,然后双方交替取球,直到取完;被迫拿到最后一个球的一方为负方(输方)。

分析:像这种只能取指定个数的问题,我们可以根据先前的答案推后面的答案.

注意:a[i] 数组是指先手取 i 个球的输赢情况,不是指A或者B

a[i] = ! ( a[i-1] && a[i-3] && a[i-7] && a[i-8] ) 

        为什么要取个非,而且各项用&呢?  思考一下,先手是我们,我们可以让后手只能走4个结果中的其中一个,所以,后手只有4条路都赢,我们才输!  都赢,都,所以用 & ,后手赢,我们就输,是个相反的结果,所以取非。

#include<bits/stdc++.h>
using namespace std;
bool a[10005];// 0 先手输 
int main()
{
	a[1]=0,a[2]=1,a[3]=0,a[4]=1,a[5]=0,a[6]=1,a[7]=1,a[8]=1;
	for(int i = 9;i<=10000;++i)a[i] = !(a[i-1] && a[i-3] && a[i-7] && a[i-8]);
    int t;scanf("%d",&t);
    while(t--){
    	int x;scanf("%d",&x);
    	printf("%d\n",a[x]);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

xingxg.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值