博弈论总结(未完成)

巴什游戏与 P-position、N-position

巴什游戏

例题

HDU-1846

有n颗石子,甲先取,乙后取,每次可以拿1~m颗石子,轮流拿,拿到最后一颗的人获胜。
输入:n和m,1<=n,m<=1000。
输出:如果先拿的甲赢了,输出"first",否则输出“second”。

分析

  1. 当n<=m时,由于一次最少拿1个,最多拿m个,所以一定是甲赢。
  2. n=m+1时,无论甲拿走多少个,乙一定能拿走剩下的全部,乙赢。

所以可以根据上面的情况扩展成下面两种情况:

  • n%(m+1)==0时,甲不管拿多少个,例如k个,乙都拿走m+1-k,总是乙赢。
  • n%(m+1)!=0,有余数r,那么,甲拿走r个,对于乙来说,就是上面那种情况的甲,总是甲赢。

参考代码

#include <bits/stdc++.h>

using namespace std;

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t, n, m;
    cin >> t;
    while (t--) {
        cin >> n >> m;
        if (n % (m + 1))cout << "first\n";
        else cout << "second\n";
    }
    return 0;
}

P-position、N-position与动态规划

例题

HDU-2147

基础概念

P-position表示前一个玩家(即刚走过一步的玩家)的必胜位置,
N-position表示下一个玩家的必胜位置。
当前状态是N-position,则表示先手必胜,P-position表示先手必败。

题目分析

题目中说到,当前玩家无法移动硬币时,会输掉比赛

  1. 只有一个格子的时候
    kiki先手,无法移动,所以先手必败
    在这里插入图片描述

  2. 硬币可以往左移,往下移,往左下移,所以当n=1,m=2或n=2,m=1或n=2,m=2的情况来说,先手必胜。
    在这里插入图片描述

  3. 这个情况可以继续向左,向下和向左下推广
    在这里插入图片描述
    从表中可以发现规律,当n和m都是奇数的时候,先手必败,否则先手必胜。

参考代码

#include <bits/stdc++.h>

using namespace std;

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n, m;
    while (cin >> n >> m, n + m) {
        cout << ((n & 1 && m & 1) ? "What a pity!\n" : "Wonderful!\n");
    }
    return 0;
}

尼姆游戏

巴什游戏是只有一堆石头,而尼姆游戏是多堆石头。

结论

假设有n堆石头,数量分别是{a1, a2, a3, ……, an},两个玩家轮流拿石子,每次从任意一堆中拿出任意数量的石子,拿到最后一个石子的获胜
若a1 ^ a2 ^ a3 ^ … ^ an != 0 则先手必胜,为N-position
若a1 ^ a2 ^ a3 ^ … ^ an == 0 则先手必败,为P-position
^ 为异或运算。

例题

HDU-1850

分析

基本原理就是使用尼姆游戏的结论,不过题目要求输出方案数,所以我们要进一步思考一下,在什么情况下算是一种方案?

假设有三堆石头,石子数量分别为5,7,9
它们的异或结果不为零,所以先手一定胜利。

根据上一小节动态规划的思想,我们可以假设,先手要创造一个让后手必败的局面,也就是说,先手从某一堆里拿出一部分石子后,让剩余几堆石子数量的异或结果为0。

比如,我们想在5中拿出去一些石子,然后让剩下的异或结果为0,但是我们发现,7和9异或后是14,我们怎么在5中取,都无法让5变成14 。

如果从7中取,我们发现,5和9异或结果是12,同样7也不能变成12

所以,我们试试从9中取。5和7的异或结果为2,这时,只要我们从9个石子中取出7个石子,剩下的石子异或结果就为0了,所以这是一个方案,结果为1 。

参考代码

#include <bits/stdc++.h>

using namespace std;

int main() {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int m, a[110];
	while (cin >> m, m) {
		int sum = 0;
		for (int i = 0; i < m; i++) {
			cin >> a[i];
			sum ^= a[i];
		}
		if (sum == 0)cout << "0\n";
		else {
			int cnt = 0;
			for (int i = 0; i < m; i++) {
				if ((sum ^ a[i]) <= a[i]) cnt++;
			}
			cout << cnt << '\n';
		}
	}
	return 0;
}

图游戏与Sprague-Grundy函数

Sprague-Grundy函数是一种比用P-position和N-position分析更高级的分析方法,该方法利用图来作为分析工具。

图游戏

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值