博弈论做题总结1
关于博弈论的基本内容详见本人这篇博客博客链接
这里以例题,补充一些博弈论的其他知识和做题的注意点。
Calendar Game(日期)
题目大意
两个人玩一种游戏,游戏内容为:在1900年1月1日到2001年11月4日之间的某一个日期开始,分别往下报日期,有两种报法,可以报该天的下一天,也可以报下个月的这一天(如果下个月没有这一天则不可以这样报),最后报到2001年11月4日的人赢。问对于给定的日期,先手是赢是输。(两人均采用最优策略)
分析
- 一个博弈论的相关定理:
我们假设 所有终结点 是 必败点P。
无论如何操作,必败点P 都只能进入 必胜点 N。(不管怎么做,都会使对手处于必胜状态)
从任何必胜点N 操作,至少有一种方式可以进入必败点 P。(当前状态 采取 最优策略 可以使对手 处于必败状态)
假设我站在先手的状态:
(注意: a -> b, 如果b全部是必胜态, 则a是必败态, 如果存在一个b是必败态,则a是必胜态。) - 通常我们分析必胜点和必败点都是以终结点进行逆序分析。
看到该题,我们不妨直接打表试图寻找规律。
必败点 | 必胜点 |
---|---|
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 |
发现:
- 2.28是一个必胜点,2.29是一个必败点,这俩是固定的
- 不管是月份加一,还是日期加一,都改变了奇偶性(必胜点 月份加日期 = 偶数, 必败点两者相加为 奇数)只有两个点不同:9.30, 11.30为必胜点 例外
代码
#include <iostream>
#include <algorithm>
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.(不符合时,交换即可)
- 如果 a = b 或者 a % b = 0这两种情况对于先手都是必胜态。
- 当 a >= 2b, 对于先手来说, 一定可以得到 a % b 的值,
如果 b % (a % b) = 0,后手必胜。
先手可以把两数变成 a % b + b 和 b, 后手 只能将两数变成 a % b 和 b。先手再次处于必胜状态。 - b < a < 2 * b,进行模拟,直到出现必胜态,通过记录操作次数(奇偶)来判断谁赢。
代码
#include <iostream>
#include <algorithm>
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 <iostream>
#include <algorithm>
using namespace std;
int main(void){
int n;
while(scanf("%d", &n) != EOF){
if(n % 3) puts("Kiki");
else puts("Cici");
}
return 0;
}