博弈论算法Java,《博弈论全家桶》(ACM / OI)(超全的博弈论 / 组合游戏大合集)...

整理的算法模板合集: ACM模板

实际上是一个全新的精炼模板整合计划

我更愿称之为组合游戏hhh

0x00 公平组合游戏ICG

若一个游戏满足:

由两名玩家交替行动

在游戏进程的任意时刻,可以执行的合法行动与轮到哪名玩家无关

游戏中的同一个状态不可能多次抵达,游戏以玩家无法行动为结束,且游戏一定会在有限步后以非平局结束

则称该游戏为一个公平组合游戏。

例如 Nim 博弈属于公平组合游戏,而普通的棋类游戏,比如围棋,就不是公平组合游戏。因为围棋交战双方分别只能落黑子和白子,胜负判定也比较复杂,不满足条件2和条件3。

0x01 有向图游戏(博弈图)

给定一个有向无环图,图中有一个唯一的起点,在起点上放有一枚棋子。两名玩家交替地把这枚棋子沿有向边进行移动,每次可以移动一步,无法移动者判负。该游戏被称为有向图游戏。

任何一个公平组合游戏都可以转化为有向图游戏。具体方法是,把每个局面看成图中的一个节点,并且从每个局面向沿着合法行动能够到达的下一个局面连有向边。 转化为有向图游戏,也称绘制了它的博弈状态图(简称博弈图或游戏图)。

这样,对于组合游戏中的每一次对弈(每一局游戏),我们都可以将其抽象成游戏图中的一条从某一顶点到出度为 0 00 的点的路径。

组合游戏向图的转化,并不单单只是为了寻找一种对应关系,它可以帮助我们淡化游戏的实际背景,强化游戏的数学模型,更加突出游戏 的数学本质。

0x02 先手必胜和先手必败

先手必胜状态 : 先手行动以后,可以让剩余的状态变成必败状态 留给对手(下一步是对手(后手)的局面)。

先手必败状态 : 不管怎么操作,都达不到必败状态,换句话说,如果无论怎么行动都只能达到一个先手必胜状态留给对手,那么对手(后手)必胜,先手必败。

简化一下就是:

先手必胜状态:可以走到某一个必败状态

先手必败状态:走不到任何一个必败状态

因为我们当前走到的状态是送给对手的状态hhh

通常先手是 A l i c e \tt AliceAlice ,后手是B o b \tt BobBob 。

定理:

定理 2.1: 没有后继状态的状态是必败状态。

定理 2.2: 一个状态是必胜状态当且仅当存在至少一个必败状态为它的后继状态。

定理 2.3: 一个状态是必败状态当且仅当它的所有后继状态均为必胜状态。

如果博弈图是一个有向无环图,则通过这三个定理,我们可以在绘出博弈图的情况下用 O ( N + M ) O(N+M)O(N+M) 的时间(其中 N NN 为状态种数,M MM 为边数)得出每个状态是必胜状态还是必败状态。

0x03 必胜点和必败点

必败点(P点) 前一个(previous player)选手将取胜的点称为必败点

必胜点(N点) 下一个(next player)选手将取胜的点称为必胜点

(1) 所有终结点是必败点(P点)

(2) 从任何必胜点(N点)操作,至少有一种方法可以进入必败点(P点)

(3)无论如何操作, 从必败点(P点)都只能进入必胜点(N点)

0x04 有向图的核

给定一张DAG图 < V , E > ,如果 V VV 的一个点集 S SS 满足:

S SS 是独立集(集合内的点互不相通)

集合 V − S V-SV−S 中的点都可以通过一步到达集合 S SS 中的点( V − S V-SV−S 指 S SS 在 V VV 中的补集)

则称 S SS 是图 V VV 的一个核。

结论: 核内节点对应 SG 组合游戏的必败态

因为 Alice 当前的棋子在 S SS 中,由于 S SS 是独立集,也就意味着 Alice 只能将棋子从 S SS 移动到 V − S V-SV−S

而 Bob又可以通过一步移动将棋子从 V − S V-SV−S 移动到了 S SS,这样 Alice 就好像是被支配了一样,被迫把棋子移动到了没有出度的必败节点,Alice 必败,Bob必胜!

0x10 几个经典组合游戏

0x11 尼姆游戏 N i m   G a m e \tt Nim\ GameNimGame

题目大意

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

我们把这种游戏称为 Nim 博弈。把游戏过程中面临的状态称为局面。整局游戏第一个行动的称为先手,第二个行动的称为后手。若在某一局面下无论采取何种行动,都会输掉游戏,则称该局面必败。

所谓采取最优策略是指,若在某一局面下存在某种行动,使得行动后对手面临必败局面,则优先采取该行动。同时,这样的局面被称为必胜。我们讨论的博弈问题一般都只考虑理想情况,即两人均无失误,都采取最优策略行动时游戏的结果。

Nim 博弈不存在平局,只有先手必胜和先手必败两种情况。

Solution

通过绘制博弈图,可以在 O ( Π i = 1 n   a i ) \mathcal O(\Pi _{i=1}^n\ a_i)O(Πi=1n​ai​) 的时间内求出该局是否先手必赢 but,这个时间复杂度太高了

结论:定义 Nim 和为 a 1 ⊕ a 2 ⊕ . . . ⊕ a n a_1\oplus a_2\oplus...\oplus a_na1​⊕a2​⊕...⊕an​。

当且仅当 N i m NimNim 和为 0 00 时,该状态为先手必败状态 否则该状态为先手必胜状态

考虑证明:

我们只需要证明 a 1 ⊕ a 2 ⊕ . . . ⊕ a n ≠ 0 a_1\oplus a_2\oplus...\oplus a_n\not=0a1​⊕a2​⊕...⊕an​​=0 是先手必胜状态,a 1 ⊕ a 2 ⊕ . . . ⊕ a n = 0 a_1\oplus a_2\oplus...\oplus a_n=0a1​⊕a2​⊕...⊕an​=0 是先手必败状态即可。

我们可以从先手必败和先手必胜状态的定义出发,也就是说我们只需要证明下面的三个定理即可(博弈论结论证明都是朝着这个方向证明的):

定理1: 没有后继状态的状态为必败状态

定理2: 对于 a 1 ⊕ a 2 ⊕ . . . ⊕ a n ≠ 0 a_1\oplus a_2\oplus...\oplus a_n\not=0a1​⊕a2​⊕...⊕an​​=0 的局面,一定存在某种移动使得 a 1 ⊕ a 2 ⊕ . . . ⊕ a n = 0 a_1\oplus a_2\oplus...\oplus a_n=0a1​⊕a2​⊕...⊕an​=0 (必胜状态一定能到达一个必败状态)

定理3: 对于 a 1 ⊕ a 2 ⊕ . . . ⊕ a n = 0 a_1\oplus a_2\oplus...\oplus a_n=0a1​⊕a2​⊕...⊕an​=0 的局面,一定不存在某种移动使得 a 1 ⊕ a 2 ⊕ . . . ⊕ a n ≠ 0 a_1\oplus a_2\oplus...\oplus a_n \neq0a1​⊕a2​⊕...⊕an​​=0 (必败状态一定不能到达任何必败状态)

定理1证明: 没有后继状态的状态(必败点)只有一个,即全 0 00 局面 此时 a 1 ⊕ a 2 ⊕ . . . ⊕ a n = 0 a_1\oplus a_2\oplus...\oplus a_n=0a1​⊕a2​⊕...⊕an​=0。定理1得证 □

定理2证明: 我们不妨假设 a 1 ⊕ a 2 ⊕ . . . ⊕ a n = k ≠ 0 a_1\oplus a_2\oplus...\oplus a_n=k\not=0a1​⊕a2​⊕...⊕an​=k​=0,如果我们将 a i a_iai​ 改为 a i ′ a_i'ai′​ ,则 a i ′ = a i ⊕ k a_i'=a_i\oplus kai′​=ai​⊕k。

设 k kk 在二进制下最高位的 1 11 在第 x xx 位,则一定有奇数个 a i a_iai​ 的二进制下在 x xx 位为 1 11(异或运算,相同为 1 11 )。

因为 a i ⊕ k = a i ′ < a i a_i\oplus k=a_i' a i ′ a_i>a_i'ai​>ai′​,所以有足够的石子去拿),则 a i a_iai​ 就变成了 a i − ( a i − a i ⊕ k ) = a i ⊕ k = a i ′ a_i-(a_i-a_i\oplus k)=a_i\oplus k=a_i'ai​−(ai​−ai​⊕k)=ai​⊕k=ai′​

则剩下的 Nim 和为 a 1 ⊕ a 2 ⊕ ⋯ ⊕ a i ⊕ k ⊕ a i + 1 ⊕ ⋯ ⊕ a n = k ⊕ k = 0 a_1\oplus a_2\oplus \cdots \oplus a_i\oplus k\oplus a_{i+1}\oplus\cdots \oplus a_n=k\oplus k=0a1​⊕a2​⊕⋯⊕ai​⊕k⊕ai+1​⊕⋯⊕an​=k⊕k=0,定理2得证 □

定理3证明: 当前状态为 a 1 ⊕ a 2 ⊕ . . . ⊕ a n = 0 a_1\oplus a_2\oplus...\oplus a_n=0a1​⊕a2​⊕...⊕an​=0 ,我们仅需证明不管怎么拿,Nim 和都不会为 0 00 。

我们使用反证法:

假设我们从 a i a_iai​ 拿走若干石子变为 a i ′ a_i'ai′​ ,使得Nim和变为 0 00 。

则说明存在:

a 1 ⊕ a 2 ⊕ ⋯ ⊕ a i ′ ⊕ a i + 1 ⊕ ⋯ ⊕ a n = 0 a_1\oplus a_2\oplus \cdots \oplus a_i' \oplus a_{i+1}\oplus\cdots \oplus a_n =0a1​⊕a2​⊕⋯⊕ai′​⊕ai+1​⊕⋯⊕an​=0

但我们知道当前状态 Nim和为 0 00,即:

a 1 ⊕ a 2 ⊕ ⋯ ⊕ a i ⊕ a i + 1 ⊕ ⋯ ⊕ a n = 0 a_1\oplus a_2\oplus \cdots \oplus a_i \oplus a_{i+1}\oplus\cdots \oplus a_n =0a1​⊕a2​⊕⋯⊕ai​⊕ai+1​⊕⋯⊕an​=0

两式异或起来得出 a i = a i ′ a_i=a_i'ai​=ai′​ ,很明显这是在原地转圈,不是合法的移动,不存在这种走法,定理3得证 □

定理 1 ~ 3 得证,故 a 1 ⊕ a 2 ⊕ . . . ⊕ a n ≠ 0 a_1\oplus a_2\oplus...\oplus a_n\not=0a1​⊕a2​⊕...⊕an​​=0 是先手必胜状态,a 1 ⊕ a 2 ⊕ . . . ⊕ a n = 0 a_1\oplus a_2\oplus...\oplus a_n=0a1​⊕a2​⊕...⊕an​=0 是先手必败状态, Nim 游戏解题结论得证。

Code

#include

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;

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

0x12 巴什博弈 B a s h   G a m e \tt Bash \ GameBashGame

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

结论

若 ( m + 1 )   ∣   n (m+1)\ |\ n(m+1)∣n (整除)则先手必败 否则先手必胜

考虑证明:

情况一: 当 n ≤ m n\le mn≤m 时,显然先手获胜

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

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

否则的话,先手可以取走模 m + 1 m+1m+1 余数个数的石头 此时转换为 ( m + 1 ) ∣ n (m+1)|n(m+1)∣n 的局面 送给后手,这样后手变成了先手,也就就是后手必败。

Code

可以直接模拟

int main() {

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

}1

2

3

4

5

6

7

8

9

也可以转换成SG 游戏使用 SG 函数 (SG函数见下节)

bool vis[N];

int sg[N];

void SG(int n, int m) { for (int i = 0; i <= n; ++i) { memset(vis, 0, sizeof(vis)); for (int j = max(i - m, 0), j < i; ++j) vis[sg[j]] = 1; for (int j = 0; j <= n; ++j) if (!vis[j]) { sg[i] = j; break; } }

}

int main() {

scanf("%d%d", &n, &m); SG(n, m); puts(sg[n] ? "first win" : "second win"); return 0;

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

0x13 威佐夫博弈 W y t h o f f   G a m e \tt Wythoff\ GameWythoffGame

Problem A 取石子游戏(POJ 1063)

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

Solution

一共只有两堆石子,我们可以把问题放到到二维坐标系上,设 x , y x,yx,y 分别对应两堆的石子的数量。

模拟发现显然 ( 0 , 0 ) (0,0)(0,0) 先手必败,我们将先手必败节点称为 奇异节点。

可以发现奇异节点上下左右四个结点,以及右上和右下的两个结点都不是奇异节点。

也就意味着如果 Alice 不在奇异节点上,那么 Alice 可以通过一步操作到达奇异节点,把奇异结点留给 Bob ,这样 Bob 必败。

我们可以发现 ( 1 , 2 ) , ( 3 , 5 ) (1,2),(3,5)(1,2),(3,5) 等等也都是奇异节点。

经过奇异节点的 3 33 条直线上的点,都能通过一步到达奇异节点。

发现这就建立起了一个有向图的核的模型。

考虑引入 Beatty定理

如果两个无理数 a , b a,ba,b 满足:

1 a + 1 b = 1 \cfrac{1}{a}+\cfrac{1}{b}=1a1​+b1​=1

那么对于两个集合 A , B A,BA,B:

A = { ⌊ n a ⌋ } , B = { ⌊ n b ⌋ } , n ∈ Z A=

{⌊na⌋}{⌊na⌋}

{⌊nb⌋}{⌊nb⌋}

A={⌊na⌋​},B={⌊nb⌋​},n∈Z

有下面两个结论:

A ∩ B = ∅ , A ∪ B = N + A\cap B=\varnothing,A\cup B=N^+A∩B=∅,A∪B=N+ (结论证明)

打表 发现 B i − A i = i B_i−A_i=iBi​−Ai​=i

可得 b = a + 1 b=a+1b=a+1 ,带入 1 a + 1 b = 1 \cfrac{1}{a}+\cfrac{1}{b}=1a1​+b1​=1 解得 a = 5 + 1 2 , b = 3 − 5 2 a=\cfrac{\sqrt{5}+1}{2},b=\cfrac{3-\sqrt{5}}{2}a=25​+1​,b=23−5​​

最终得出威佐夫博弈结论:假设两堆石子为 ( a , b ) (a,b)(a,b)(其中a < b a

那么先手必败,当且仅当 ( b − a ) × ( 5 + 1 ) 2 = a (b-a)\times \cfrac{(\sqrt{5}+1)}{2}=a(b−a)×2(5​+1)​=a

其中的 ( 5 + 1 ) 2 \cfrac{(\sqrt{5}+1)}{2}2(5​+1)​ 实际就是黄金分割数 1.618 1.6181.618,细思极恐,细思极恐…

Code

int main() {

scanf("%d%d", &n, &m); if (a > b) swap(a, b); int ans = (b - a) * ((1.0 + sqrt(5.0)) / 2.0); if (ans == a) puts("0"); else puts("1"); return 0;

}1

2

3

4

5

6

7

8

9

10

11

Problem A Game of Taking Stones(2017 ACM ICPC dalian C)

给你两个石堆的石头数量 a , b a,ba,b,两个人轮流拿,两人轮流从任意一堆取至少一个或者从两堆取同样多的物品。问你先手获胜还是后手胜。

a , b ≤ 1 0 100 a,b\le10^{100}a,b≤10100

威佐夫博弈模板题,但是数据开到了 1 0 100 10^{100}10100,普通的C++高精会炸,所以直接用Java就行了(而且Java比C++高精好写多了 ~ )

我们直接用 Java 二分求出 5 \sqrt{5}5​,设 b > a b>ab>a 计算 ( b − a ) × ( 5 + 1 ) 2 \cfrac{(b-a)\times (\sqrt{5}+1)}{2}2(b−a)×(5​+1)​ 即可。

Code

import java.math.*;

import java.util.*;

public class Main { public static void main(String[] args) { BigDecimal one = BigDecimal.valueOf(1); BigDecimal two = BigDecimal.valueOf(2), five = BigDecimal.valueOf(5); BigDecimal t = one.add(sqrt(five, 500)).divide(two); Scanner sc = new Scanner(System.in); while (sc.hasNext()) { BigDecimal a, b, tmp = null; a = sc.nextBigDecimal(); b = sc.nextBigDecimal(); if (a.compareTo(b) > 0) { tmp = a; a = b; b = tmp; } if (b.subtract(a).multiply(t).setScale(0, BigDecimal.ROUND_DOWN).equals(a)) { System.out.println(0); } else System.out.println(1); } sc.close(); } private static BigDecimal sqrt(BigDecimal x, int n) { BigDecimal l = BigDecimal.ZERO, r = x, mid; BigDecimal two = BigDecimal.valueOf(2); for (int i = 0; i <= n; i++) { mid = l.add(r).divide(two); if (mid.pow(2).compareTo(x) <= 0) l = mid; else r = mid; } return l; }

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

0x14 斐波那契博弈 F i b o n a c c i   G a m e \tt Fibonacci\ GameFibonacciGame

有一堆个数为 n ( n ≥ 2 ) n(n\ge 2)n(n≥2)的石子,游戏双方轮流取石子,规则如下:

先手不能在第一次把所有的石子取完,至少取 1 11 颗;

之后每次可以取的石子数至少为 1 11,至多为对手刚取的石子数的 2 22 倍。

约定取走最后一个石子的人为赢家,求必败态。

结论:

先手必败,当且仅当石子数为斐波那契数

先证明必要性,斐波那契数一定先手必败,可以用数学归纳法,大致思路就是一定能拆成两堆斐波那契数,不论先手怎样取,后手总能取到最后一颗

然后证明充分性,由定理:任何正整数可以表示为若干个不连续的Fibonacci数之和,那么就回到了斐波那契数列里

#include

#include

#include

#include

#include

using namespace std;

const int N = 50007;

int f[N], x;

mapmp;

int main()

{

fib[1] = 1;

fib[2] = 1;

for(int i = 3;i <= 50; ++ i) f[i] = f[i-1] + f[i-2], mp[f[i]] = 1;

while(scanf("%d", &x) && x != 0)

puts(mp[x] == 1 ? "Second win" : "First win"); return 0;

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

0x20 SG函数

0x21 前置知识: M e x MexMex 运算

设 S SS 表示一个非负整数集合。定义 m e x ( S ) mex(S)mex(S) 为求出不属于集合S的最小非负整数的运算,即:

m e x ( S ) = m i n { x } mex(S) = min\{x\}mex(S)=min{x},x xx 属于自然数,且 x xx 不属于 S SS 。

0x22 SG函数

SG函数是对游戏图中每一个节点的评估函数。

规定游戏终点的 SG 函数值定为 0 00,即 S G ( 终 点 ) = 0 SG(终点)=0SG(终点)=0。

在有向图游戏中(任何一个博弈都可以转换为一个有向图游戏),对于每个节点 x xx (局面),设从 x xx 出发共有 k kk 条有向边(合法的操作),分别到达节点 y 1 , y 2 , … , y k y_1, y_2, …, y_ky1​,y2​,…,yk​(下一个局面) ,定义 S G ( x ) SG(x)SG(x) 为 x xx 的后继节点(注意只是一层的后继结点) y 1 , y 2 , … , y k y_1, y_2, …, y_ky1​,y2​,…,yk​ 的 S G SGSG 函数值构成的集合再执行 m e x ( S ) mex(S)mex(S) 运算的结果,即:

S G ( x ) = m e x ( { S G ( y 1 ) , S G ( y 2 ) , … , S G ( y k ) } ) SG(x) = mex(\{SG(y1), SG(y2), …, SG(yk)\})SG(x)=mex({SG(y1),SG(y2),…,SG(yk)})

如 图20.1 所示:

博弈论算法Java,《博弈论全家桶》(ACM / OI)(超全的博弈论 / 组合游戏大合集)..._第1张图片

图20.1

%2

特别地,整个有向图游戏 G GG 的 S G SGSG 函数值被定义为有向图游戏起点 s ss 的 S G SGSG 函数值,即 S G ( G ) = S G ( s ) SG(G) = SG(s)SG(G)=SG(s) 。

我们发现若 S G ( x ) = 0 SG(x)=0SG(x)=0 则为必败状态,若 S G ( x ) ≠ 0 SG(x)\not= 0SG(x)​=0,则为必胜状态。(若非零说明这个点直接指向了 0 00,也就意味着可以到达必败状态,是必胜状态)

0x23 SG 函数的性质

两个SG函数的性质:

(1)对于任意的局面,如果它的 S G SGSG 值为 0 00 ,那么它的任何一个后

继局面的SG值不为 0 00 。

(2)对于任意的局面,如果它的 S G SGSG 值不为 0 00 ,那么它一定有一个

后继局面的 S G SGSG 值为 0 00 。

SG(S p r a g u e − G r u n d y \tt Sprague-GrundySprague−Grundy)定理 :所有一般胜利下的公平组合游戏都能转化成尼姆数表达的尼姆堆博弈,一个博弈的 尼姆值 定义为这个博弈的等价尼姆数,即:对于当前游戏 X XX,它可以拆分成若干个子游戏 x 1 , x 2 , . . . , x n x_1,x_2,...,x_nx1​,x2​,...,xn​ 那么 S G ( X ) = S G ( x 1 ) ⊕ S G ( x 2 ) ⊕ . . . ⊕ S G ( x n ) SG(X)=SG(x_1)\oplus SG(x_2)\oplus...\oplus SG(x_n)SG(X)=SG(x1​)⊕SG(x2​)⊕...⊕SG(xn​)

对于由 n nn 个有向图游戏组成的组合游戏 设它们的起点分别为 s 1 , s 2 , . . . , s n s_1,s_2,...,s_ns1​,s2​,...,sn​(好多个起点,有好多个博弈图,也就是有好多个 图20.1 ) ,则当且仅当 S G ( s 1 ) ⊕ S G ( s 2 ) ⊕ . . . ⊕ S G ( s n ) ≠ 0 SG(s_1)\oplus SG(s_2)\oplus...\oplus SG(s_n)\not=0SG(s1​)⊕SG(s2​)⊕...⊕SG(sn​)​=0 时,这个游戏为先手必胜 。

也就意味着,我们将原本需要考虑博弈图的所有点,复杂度较高,但是我们通过SG函数,变成了只需要考虑起点即可。

证明方法类似尼姆游戏,略。

0x24 转换为 Nim 游戏

事实上,每一个简单SG-组合游戏都可以完全等效成一堆数目为 K KK 的石子(Nim 游戏),其中 K KK 为该简单游戏的 S G SGSG 函数值。这样的等效是充要的。

定义游戏的和: 考虑任意多个同时进行的SG-组合游戏,这些SG-组合游

戏的和是这样一个SG-组合游戏,在它进行的过程中,游戏者可以任意

挑选其中的一个 单一游戏 进行决策,最终,没有办法进行决策的人输。

定理 23.1: 在我们每次只能进行一步操作的情况下,对于任何的游戏的和,我们若将其中的任一单一SG-组合游戏换成数目为它的SG值的一堆石子, 该单一SG-组合游戏的规则变成取石子游戏的规则(可以任意取,甚至 取完),则游戏的和的胜负情况不变。

这个定理告诉我们,在考虑游戏的和时,每一个单一游戏的具体细节是可以被忽略的,我们所关心的只是SG函数值。所以我们可以将组成它的所有子游戏全部换成相应数目的一堆石子。这样,所有的游戏的和都等价成一个 Nim 游戏。

0x24 有向图游戏的和

设 G 1 , G 2 , … , G m G_1, G_2, …, G_mG1​,G2​,…,Gm​ 是 m mm 个有向图游戏。定义有向图游戏 G GG ,它的行动规则是任选某个有向图游戏 G i G_iGi​ ,并在 G i G_iGi​ 上行动一步。G GG 被称为有向图游戏G 1 , G 2 , … , G m G_1, G_2, …, G_mG1​,G2​,…,Gm​的和。

有向图游戏的和的 S G SGSG 函数值等于它包含的各个子游戏 S G SGSG 函数值的异或和,即:

S G ( G ) = S G ( G 1 ) ⊕ S G ( G 2 ) ⊕ … ⊕ S G ( G m ) SG(G) = SG(G_1) \oplus SG(G_2)\oplus …\oplus SG(G_m)SG(G)=SG(G1​)⊕SG(G2​)⊕…⊕SG(Gm​)

定理22.1: 有向图游戏的某个局面必胜,当且仅当该局面对应节点的SG函数值大于0。

定理22.2: 有向图游戏的某个局面必败,当且仅当该局面对应节点的SG函数值等于0。

我们只需要判断一下 S G ( G ) SG(G)SG(G) 即可。

Problem A 集合- Nim游戏( AcWing 893)

给定 n nn 堆石子以及一个由 k kk 个不同正整数构成的数字集合 S SS 。

现在有两位玩家轮流操作,每次操作可以从任意一堆石子中拿取石子,每次拿取的石子数量必须包含于集合 S SS ,最后无法进行操作的人视为失败。

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

每一堆输入的石子数就是每一堆的起点,答案就是所有起点的 Nim 和

假设某一堆有 10 1010 个石子,S = { 2 , 5 } S=\{2,5\}S={2,5} 那么博弈图就是从 10 1010 开始连边,如图20.1 所示。

博弈论算法Java,《博弈论全家桶》(ACM / OI)(超全的博弈论 / 组合游戏大合集)..._第2张图片

图20.2

本题的连边方式就是从集合 S SS 中选择一个数往下走(取走这么多数,往下搜 x − s u m x-sumx−sum)

我们直接记忆化搜索sg函数即可

#include

#include

#include

#include

#include

#include

using namespace std;

const int N = 507, M = 50007;

typedef long long ll;

typedef int itn;

itn n, m;

int a[N];

itn s[N], f[M];

int sg(int x)//记忆化搜索

{ if(f[x] != -1) return f[x]; unordered_setS; for(int i = 1; i <= m; ++ i) { itn sum = s[i]; if(x >= sum) S.insert(sg(x - sum)); } for(int i = 0; ; ++ i) { if(!S.count(i)) return f[x] = i; }

}

int main()

{ scanf("%d", &m); for(int i = 1; i <= m; ++ i) {// m 个起点 scanf("%d", &s[i]); } int res = 0; scanf("%d", &n); memset(f, -1, sizeof f); for(int i = 1; i <= n; ++ i) { int x; scanf("%d", &x); res ^= sg(x); } if(res == 0) puts("No"); else puts("Yes"); return 0;

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

以下为待更内容,预计将在 2021/2/21 前更完 (●ˇ∀ˇ●)

0x30 SG游戏及其拓展变形

0x 31 Anti-SG 游戏(走完最后一步者输)

我们先从最基本的 Anti-Nim 游戏开始

Anti-Nim游戏

有 n nn 堆石子,两个人可以从任意一堆石子中拿任意多个石子(不能不拿),拿走最后一个石子的人失败。问谁会胜利

看上去好像颠覆了SG游戏的规则,连胜利的条件都反了这怎么玩?

先给出结论

先手必胜当且仅当:

(1)∀ \forall∀ 所有堆的石子数都为 1 11 且游戏的SG值为 0 00 。

(2)∃ \exist∃ 有些堆的石子数大于 1 11 且游戏的SG值不为 0 00 。

考虑证明:

游戏大概可以被分为3种情况

每堆只有一个石子

当异或值(SG值)为 0 00 时(有偶数堆),先手必胜

当异或值(SG值)不为 0 00 时(有奇数堆),先手必败

只有一堆石子数大于1,先手必胜

我们发现先手可以对数量大于 1 11 的那堆石子下手,除去这堆异或值不为 0 00 ,那先手就可以把这堆变成 0 00 ,使得异或值为 0 00,使得自己先手必胜

存在至少两堆石子数大于1

当异或和为0时,先手必败

当异或和不为0时,先手必败

这一步的结论与Nim游戏非常相似,同时它们的证明也非常相似,大概就是从异或和为0的状态无论怎样都会变为异或和不为0的状态,反过来从异或和不为0的状态总有一步能到达异或和为0的状态

0x32 SJ定理

0x33 Multi-SG游戏(可以将一堆石子分成多堆)

0x34 Every-SG游戏(每一个可以移动的棋子都要移动)

0x35翻硬币游戏

0x36 无向图删边游戏

0x40 经典组合游戏拓展

0x41 巴什博奕的扩展——k倍动态减法游戏

0x42 尼姆博弈的三种扩展

0x50 寻找必败态解题

0x60 不平等博弈问题

0x70 更多例题

Problem A Euclid’s Game(POJ 1063 )

给定两个正整数 a , b a,ba,b,每次操作,可以将大的数减掉小的数的整数倍。当一个数变为 0 00 的时候结束。先将其中一个数减为 0 00 的获胜。Stan先手,问谁能赢。

Solution

把问题转化成我们熟悉的模型,相当于有一排石子堆,必须把前面的石子堆取完了才能取后面的,取最后一个石子的人赢,问谁能赢

文章来源: blog.csdn.net,作者:繁凡さん,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/weixin_45697774/article/details/113874771

你可能感兴趣的:(博弈论算法Java)