【教程】博弈论学习笔记

By zhongzijun \text{zhongzijun} zhongzijun

博弈论又被称为对策论(Game Theory)既是现代数学的一个新分支,也是运筹学的一个重要学科。
博弈论主要研究公式化了的激励结构间的相互作用。是研究具有斗争或竞争性质现象的数学理论和方法。 博弈论考虑游戏中的个体的预测行为和实际行为,并研究它们的优化策略。生物学家使用博弈理论来理解和预测进化论的某些结果。
博弈论已经成为经济学的标准分析工具之一。在金融学、证券学、生物学、经济学、国际关系、计算机科学、政治学、军事战略和其他很多学科都有广泛的应用。

一、Nim游戏和巴什博弈

先来观察两个游戏。

游戏 A (Nim游戏)

甲乙两人面对若干堆石子,其中每一堆石子的数目可以任意确定。
例如 【 图 1 】 【 图 1 】 1 所示的初始局面:共 n = 3 n=3 n=3 堆,其中第一堆的石子数 a 1 = 3 a1=3 a1=3 ,第二堆石子数 a 2 = 3 a2=3 a2=3 ,第三堆石子数 a 3 = 1 a3=1 a3=1 。两人轮流按下列规则取走一些石子,游戏的规则如下:
每一步应取走至少一枚石子;
每一步只能从某一堆中取走部分或全部石子;
如果谁无法按规则取子,就输了。

游戏 B (巴什博弈)

甲乙双方事先约定一个数 m m m ,并且每次取石子的数目不能超过 m m m 个,其余规则同 游戏 A

【教程】博弈论学习笔记_第1张图片

我们关心的是,对于一个初始局面,究竟是 先 行 者 ( 甲 ) 先行者(甲) 有必胜策略,还是 后 行 者 ( 乙 ) 后行者(乙) 有必胜策略。

下面,我们从简单入手,先来研究研究这个游戏的一些性质。

  • 用一个 n n n 元组 ( a 1 , a 2 , … , a n ) (a1, a2, …, an) (a1,a2,,an) ,来描述游戏过程中的一个局面。

  • 局面的加法

    ( a 1 , a 2 , … , a n ) + ( b 1 , b 2 , … , b m ) = ( a 1 , a 2 , … , a n , b 1 , b 2 , … , b m ) (a_1, a_2, …, a_n) + (b_1, b_2, …, b_m) = (a_1, a_2, …, a_n, b_1, b_2, …, b_m) (a1,a2,,an)+(b1,b2,,bm)=(a1,a2,,an,b1,b2,,bm)
    对于 局面A , B , S ,若 S = A + B S=A+B S=A+B ,则称局面 S 可以分解为“子局面” A 和 B 。
    局 面 ( 3 , 3 , 1 ) 局面(3, 3, 1) (3,3,1) 可以分解为 ( 3 , 3 ) (3, 3) (3,3) ( 1 ) (1) (1)
    如果初始局面可以分成两个相同的“子局面”,则乙有必胜策略。

  • 对于局面S,若先行者有必胜策略,则称“ S胜 ”。
    对于局面S,若后行者有必胜策略,则称“ S负 ”。
    A = ( 1 ) A=(1) A=(1) B = ( 3 , 3 ) B=(3, 3) B=(3,3) C = ( 2 , 2 , 5 , 5 , 5 , 5 , 7 , 7 ) C=(2, 2, 5, 5, 5, 5, 7, 7) C=(2,2,5,5,5,5,7,7) ,则我们称 A 胜 A胜 A B 负 B负 B C 负 C负 C

  • 如果 局 面 S 局面S S 胜,则必存在取子的方法 S → T S→T ST ,且 T 负 T负 T
    如果 局 面 S 局面S S 负,则对于任意取子方法 S → T S→T ST ,有 T 胜 T胜 T

  • 初 始 局 面 S 初始局面S S 可以分解成两个子局面 A A A B B B (分解理论)

  • A A A B B B 一胜一负,则 S 胜 S胜 S
    A A A B B B 负,则 S 负 S负 S
    A A A B B B 胜,则 有 时 S 胜 , 有 时 S 负 有时S胜,有时S负 SS
    如果 S = A + C + C S=A+C+C S=A+C+C ,则 S S S 的胜负情况 与 A A A 相同。
    即当 S = A + B S=A+B S=A+B B B B 负时 S S S 的胜负情况与 A A A 相同。
    【 图 1 】所示的初始局面 ( 3 , 3 , 1 ) = ( 3 ) + ( 3 ) + ( 1 ) (3, 3, 1) = (3) + (3) + (1) (3,3,1)=(3)+(3)+(1) ,与局面 ( 1 ) (1) (1) 胜 负 情 况 相 同 胜负情况相同

请认真思考一下这是为什么。

  • 图1中所示的初始局面 ( 3 , 3 , 1 ) (3, 3, 1) (3,3,1) 是“胜”局面,甲有必胜策略。
    称一个石子也没有的局面为“空局面”。
    空局面是“负”局面。

  • 如果 局 面 S 局面S S 中,存在两堆石子,它们的数目相等。用 T T T 表示从 S S S 中把这两堆石子拿掉之后的局面,则称“ S 可 以 简 化 为 T S可以简化为T ST ”。
    局面 ( 2 , 2 , 2 , 7 , 9 , 9 ) (2, 2, 2, 7, 9, 9) (2,2,2,7,9,9) 可以简化为 ( 2 , 2 , 2 , 7 ) (2, 2, 2, 7) (2,2,2,7) ,还可以进一步简化为 ( 2 , 7 ) (2, 7) (2,7)

  • 一个局面的胜负情况,与其简化后的局面相同。
    例如三个局面 ( 2 , 2 , 2 , 7 , 9 , 9 ) (2, 2, 2, 7, 9, 9) (2,2,2,7,9,9) ( 2 , 2 , 2 , 7 ) (2, 2, 2, 7) (2,2,2,7) ( 2 , 7 ) (2, 7) (2,7) ,胜负情况都相同。

  • 不能简化的局面称为“最简局面”。
    局面 ( 2 , 7 ) (2, 7) (2,7) 是最简局面。
    最简局面中不会有两堆相同的石子,故可以用一个集合来表示最简局面。
    最简局面 ( 2 , 7 ) (2, 7) (2,7) 可以用 集 合 2 , 7 集合{2, 7} 2,7 来表示。

  • 如果只关心局面的胜负,则一个局面可以用一个集合来描述。
    图1所示的局面 ( 3 , 3 , 1 ) (3, 3, 1) (3,3,1) ,可以用 集 合 1 集合{1} 1 来描述。

  • 如果用搜索(博弈树)的方法来解这个游戏,则采用集合来表示一个局面,比采用多元组来表示一个局面,搜索量将有所减少,但时间复杂度仍然很高。

  • 能不能进一步简化一个局面的表示呢?

  • 类比与联想

注意,这里的二进制加法指的是不进位的二进制加法。

二进制数的加法 VS 局面的加法
大 写 字 母 A B 大写字母AB AB 表示 局 面 局面 小 写 字 母 a b 小写字母ab ab 表示二进制数。
A 和 B 相 同 A和B相同 AB ,则 A + B A+B A+B 负;若 a 和 b 相 等 a和b相等 ab ,则 a + b 为 0 a+b为0 a+b0
A 胜 B 负 A胜B负 AB ,则 A + B 胜 A+B胜 A+B ;若 a ≠ 0 a≠0 a=0 b = 0 b=0 b=0 ,则 a + b ≠ 0 a+b≠0 a+b=0
B 胜 A 负 B胜A负 BA ,则 A + B 胜 A+B胜 A+B ;若 b ≠ 0 b≠0 b=0 a = 0 a=0 a=0 ,则 a + b ≠ 0 a+b≠0 a+b=0
A 负 B 负 A负B负 AB ,则 A + B 负 A+B负 A+B ;若 a = 0 a=0 a=0 b = 0 b=0 b=0 ,则 a + b = 0 a+b=0 a+b=0
A 胜 B 胜 A胜B胜 AB ,则 A + B 有 时 胜 , 有 时 负 A+B有时胜,有时负 A+B
a ≠ 0 a≠0 a=0 b ≠ 0 b≠0 b=0 ,则有时 a + b ≠ 0 a+b≠0 a+b=0 ,有时 a + b = 0 a+b=0 a+b=0
……

  • 如果用 二 进 制 数 s 二进制数s s 来表示一个 局 面 S 局面S S 胜 或 负 胜或负 S 胜 S胜 S s ≠ 0 s≠0 s=0 S 负 S负 S s = 0 s=0 s=0
    局面的加法,与二进制数的加法,性质完全相同。
    能否用一个二进制数,来表示一个局面呢?
    KaTeX parse error: Expected 'EOF', got '#' at position 3: 符号#̲S ,表示局面S所对应的二进制数。

  • 如果 局 面 S 局面S S 只有一堆石子,则用这一堆石子数目所对应的 二 进 制 数 二进制数 来表示 S S S

  • 例如 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲(5)=5=101

  • 若局面 S = A + B S=A+B S=A+B ,则 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲S=#A+#B
    局面 ( 3 , 3 ) = ( 3 ) + ( 3 ) (3, 3)=(3)+(3) (3,3)=(3)+(3) ,所以 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲(3, 3)=#(3)+#(3…
    局面 ( 3 , 3 , 1 ) = ( 3 , 3 ) + ( 1 ) (3, 3, 1)=(3, 3)+(1) (3,3,1)=(3,3)+(1) ,所以 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲(3, 3, 1)=#(3, …

  • 函数f :若 局 面 S 局面S S 只有一堆石子,设 S = a 1 S={a1} S=a1 ,则 KaTeX parse error: Expected 'EOF', got '#' at position 7: f(a1)=#̲S ,即 KaTeX parse error: Expected 'EOF', got '#' at position 7: f(a1)=#̲(a1)
    对于 游 戏 A 游戏A A 来说, KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲(5)=101 ,所以 f ( 5 ) = 101 f(5)=101 f(5)=101
    对于 游 戏 A 游戏A A 来说, f ( x ) f(x) f(x) 就是 x x x 所对应的 二 进 制 数 二进制数 。换句话说, f ( x ) = x f(x)=x f(x)=x
    游 戏 B 游戏B B f 函 数 f函数 f 则为 : f ( x ) = x m o d ( m + 1 ) f(x)=x mod (m+1) f(x)=xmod(m+1)

  • 设局面 S = ( a 1 , a 2 , … , a n ) S=(a1, a2, …, an) S=(a1,a2,,an) ,即 S = ( a 1 ) + ( a 2 ) + … + ( a n ) S=(a1)+(a2)+…+(an) S=(a1)+(a2)++(an) ,则 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲S=f(a1)+f(a2)+…… 。例如 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲(3, 3, 1)=#((3)…

  • 对于局面S,若#S=0,则S负;若#S≠0,则S胜。

  • 二进制数 a, b ,若 a + b = 0 ,当且仅当 a = b 。
    二进制数 a, b, s ,若 a + b = s ,则 a = b + s 。

  • 二进制数 a 1 + a 2 + … + a n = p ≠ 0 a1+a2+…+an=p≠0 a1+a2++an=p=0 ,则 必 存 在 k 必存在 k k ,使得 a k + p < a k ak+p < ak ak+p<ak
    因为 p ≠ 0 p≠0 p=0 ,所以 p p p 的最高位是 1 1 1
    p p p 最 高 位 最高位 第 q 位 第q位 q
    至 少 存 在 一 个 k 至少存在一个k k ,使得 a k ak ak 第 q 位 第q位 q 也是 1 1 1
    a k + p ak+p ak+p 第 q 位 第q位 q 0 0 0 ,所以 a k + p < a k ak+p < ak ak+p<ak

  • KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲S=0 ,则无论先行者如何取子 S → T S→T ST ,都有 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲T≠0
    先行者只能从某一堆中取若干石子,不妨设他选择的就是第1堆;
    设先行者从第1堆中取了x个石子,用T表示取完之后的局面;
    设S=(a1, a2, …, an),则T=(a1–x, a2, …, an);
    KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲S=f(a1)+#(a2, ……,故f(a1)=#(a2, …, an);
    KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲T=f(a1–x)+#(a2,…
    x>0→f(a1)≠f(a1–x)→f(a1)+f(a1–x)≠0→#T≠0。

  • KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲S≠0 ,则先行者必然存在一种取子方法 S → T S→T ST ,且 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲T=0
    S = ( a 1 , a 2 , … , a n ) S=(a1, a2, …, an) S=(a1,a2,,an)KaTeX parse error: Expected 'EOF', got '#' at position 3: p=#̲S=f(a1)+f(a2)+……
    因为 p ≠ 0 p≠0 p=0 ,所以 必 然 存 在 k 必然存在k k ,使得 f ( a k ) + p < f ( a k ) f(ak)+pf(ak)+p<f(ak) ,不妨设 k = 1 k=1 k=1 f ( a 1 ) + p = x f(a1)+p=x f(a1)+p=x
    先行者将 第 1 堆 第1堆 1 的石子的数目从 a 1 a1 a1 变成 x x x ,用 T T T 表示 这 个 局 面 这个局面
    KaTeX parse error: Expected 'EOF', got '#' at position 3: p=#̲S=f(a1)+#(a2, ……,故 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲(a2, …, an)+2f(… (两个相同的数相加等于 0 );
    KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲T=f(x)+#(a2, …,…

  • S S S 空 局 面 空局面 ,则 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲S=0

  • KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲S=0 ,则 S 负 S负 S ;若 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲S≠0 ,则 S 胜 S胜 S

  • KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲(1, 2, 3)=01+10…,故 局 面 ( 1 , 2 , 3 ) 负 局面(1, 2, 3)负 (1,2,3)
    KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲(1, 2, 3, 4)=00…,故 局 面 ( 1 , 2 , 3 , 4 ) 胜 局面(1, 2, 3, 4)胜 (1,2,3,4)

  • 对于 游 戏 A 游戏A A 来说,任意的一个 初 始 局 面 S = ( a 1 , a 2 , … , a n ) 初始局面S=(a1, a2, …, an) S=(a1,a2,,an) ,我们把这里的 a i ai ai 都看成是 二 进 制 数 二进制数 。令 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲S=a1+a2+…+an 。若 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲S≠0 ,则 先 行 者 ( 甲 ) 先行者(甲) 有必胜策略;否则 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲S=0 ,这时 后 行 者 ( 乙 ) 后行者(乙) 有必胜策略。
    下面把这个结论推广到 游 戏 B 游戏B B

  • 对于任意初始局面 S = ( a 1 , a 2 , … , a n ) S=(a1, a2, …, an) S=(a1,a2,,an) ,令 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲S=f(a1)+f(a2)+……
    KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲S≠0 ,则 先 行 者 ( 甲 ) 先行者(甲) 有必胜策略;否则 后 行 者 ( 乙 ) 后行者(乙) 有必胜策略。
    类似 游 戏 A 游戏A A 的证明。
    游 戏 B 游戏B B 的解法与 游 戏 A 游戏A A 十分类似。这是因为两个游戏的规则相当类似。

二、威佐夫博奕

例题

有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。

  • 关键在于判断初始局面是不是奇异局势。是则先手输,否则先手赢。

  • 定义:对于形如 ( a k , b k ) ( a k ≤ b k , k = 0 , 1 , 2 , … , n ) (a_k,b_k)(a_k ≤ b_k ,k=0,1,2,…,n) (ak,bk)(akbk,k=012,n) a k a_k ak 是前面没有出现过的最小的自然数 , b k = a k + k b_k=a_k+k bk=ak+k 的局势叫做奇异局势。

  • 例子:例如 ( 0 , 0 ) (0,0) (0,0) ( 1 , 2 ) (1,2) (1,2) ( 3 , 5 ) (3,5) (3,5) ( 4 , 7 ) (4,7) (4,7) ( 6 , 10 ) (6,10) (6,10) ( 8 , 13 ) (8,13) (8,13) ( 9 , 15 ) (9,15) (9,15) ( 11 , 18 ) (11,18) (11,18) ( 12 , 20 ) (12,20) (12,20) 都是奇异局势。

  • 判断:当 min ⁡ ( a , b ) = 1 2 ∣ b − a ∣ ( 5 + 1 ) \min(a,b)=\frac{1}{2}|b-a|(\sqrt{5}+1) min(a,b)=21ba(5 +1) 时,局面 ( a , b ) (a,b) (a,b) 是奇异局势。

  • 性质

  1. 任何自然数都包含在一个且仅有一个奇异局势中。
    由于 a [ k ] a[k] a[k] 是未在前面出现过的最小自然数,所以有 a [ k ] > a [ k − 1 ] a[k] > a[k-1] a[k]>a[k1] ,而 b [ k ] = a [ k ] + k > a [ k − 1 ] + k > a [ k − 1 ] + k − 1 = b [ k − 1 ] > a [ k − 1 ] b[k]= a[k] + k > a[k-1] + k > a[k-1] + k - 1 = b[k-1] > a[k-1] b[k]=a[k]+k>a[k1]+k>a[k1]+k1=b[k1]>a[k1] 。所以 性 质 1 性质1 1 成立。

  2. 任意操作都可将奇异局势变为非奇异局势。
    事实上,若只改变奇异局势 ( a [ k ] , b [ k ] ) (a[k],b[k]) (a[k],b[k]) 的某一个分量,那么另一个分量不可能在其他奇异局势中,所以必然是非奇异局势。如果使 ( a [ k ] , b [ k ] ) (a[k],b[k]) (a[k]b[k]) 的两个分量同时减少,则由于其差不变,且不可能是其他奇异局势的差,因此也是非奇异局势。

  3. 采用适当的方法,可以将非奇异局势变为奇异局势。
    假设面对的局势是 ( a , b ) (a,b) (a,b) ,若 b = a b = a b=a ,则同时从两堆中取走 a a a 个物体,就变为了奇异局势 ( 0 , 0 ) (0,0) (0,0) ;如果 a = a [ k ] , b > b [ k ] a = a[k] , b > b[k] a=a[k],b>b[k] 那么,取走 ( b − b [ k ] ) (b - b[k]) (bb[k]) 个物体,即变为奇异局势;如果 a = a [ k ] , b < b [ k ] a = a[k] , b < b[k] a=a[k],b<b[k] 则同时从两堆中拿走 a − a [ b − a ] a-a[b-a] aa[ba](注:这里 ( b − a ) (b-a) (ba) a a a 的下标) 个物体变为奇异局势( a [ b − a ] , b − a + a [ b − a ] a[b-a] , b-a+a[b-a] a[ba],ba+a[ba] );如果 a > a [ k ] , b = a [ k ] + k a > a[k] , b= a[k] + k a>a[k],b=a[k]+k 则从第一堆中拿走多余的数量 ( a − a [ k ] ) (a - a[k]) (aa[k]) 即可;如果 a < a [ k ] , b = a [ k ] + k a < a[k] , b= a[k] + k a<a[k],b=a[k]+k ,分两种情况,第一种, a = a [ j ] ( j < k ) a=a[j] (j < k) a=a[j](j<k) 从第二堆里面拿走 ( b − b [ j ] ) (b - b[j]) (bb[j]) 即可;第二种, a = b [ j ] ( j < k ) a=b[j] (j < k) a=b[j](j<k) 从第二堆里面拿走 ( b − a [ j ] ) (b - a[j]) (ba[j]) 即可。

三、SG函数

s g 函 数 sg函数 sg 是定义在组合游戏上的函数,用 g ( X ) g(X) g(X) 表示 状 态 X 状态X X 的函数值, F ( x ) F(\text{x}) F(x) 表示 局 面 X 局面X X 的后继状态,即如果能够通过 局 面 X 局面X X 直接到达 局 面 Y 局面Y Y ,那么 Y ∈ F ( x ) Y \in F(\text{x}) YF(x) 。它的定义如下:

mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如 mex{0,1,2,4}=3 、 mex{2,3,5}=0 、 mex{}=0 。

“?”符号指的是补集。补集一般指绝对补集,即一般地,设 S 是一个集合, A 是 S 的一个子集,由 S 中所有不属于 A 的元素组成的集合,叫做子集 A 在 S 中的绝对补集。在集合论和数学的其他分支中,存在补集的两种定义:相对补集和绝对补集。

g ( X ) = mex ( F ( x ) ) = min ⁡ ( n ∣ n ∈ ? N F ( x ) ) g(X)=\text{mex}(F(\text{x})) =\min(n|n∈?NF(\text{x})) g(X)=mex(F(x))=min(nn?NF(x))

  • 定理和证明1

对于 X = X 1 + X 2 + . . . + X n X=X_1+X_2+...+X_n X=X1+X2+...+Xn ,有 g ( X ) = g ( X 1 ) ⊕ g ( X 2 ) ⊕ . . . ⊕ g ( X n ) g(X)=g(X_1)⊕g(X_2)⊕...⊕g(X_n) g(X)=g(X1)g(X2)...g(Xn)

b = g 1 ( X 1 ) ⊕ g 2 ( X 2 ) ⊕ . . . ⊕ g n ( X n ) b=g1(X_1)⊕g2(X_2)⊕...⊕gn(X_n) b=g1(X1)g2(X2)...gn(Xn) ,那么我们需要证明:
1)对于任意 a ∈ N a∈N aN a < b a < b a<b,一定存在 X ′ ∈ F ( X ) X'∈F(X) XF(X) 使 g ( X ′ ) = a g(X')=a g(X)=a
2)对于任意 X ′ ∈ F ( X ) X'∈F(X) XF(X) ,那么 G ( X ′ ) ≠ b G(X')≠b G(X)=b

  • 定理和证明2

对于任意 X ′ ∈ F ( X ) X'∈F(X) XF(X) ,那么 G ( X ′ ) ≠ b G(X')≠b G(X)=b
对于一个任意的的 X X X 存在 X ’ ∈ F ( X ) X’∈F(X) XF(X) 使 g ( X ’ ) = b g(X’)=b g(X)=b

那么令 X ’ = ( X 1 , … , X i ’ , … , X n ) , g ( X ’ ) = g 1 ( X 1 ) ⊕ … g i ( X i ’ ) … g n ( X n ) = b X’=(X_1,…,X_i’,…,X_n),g(X’)=g1(X_1)⊕ …gi(X_i’)…gn(X_n)=b X=(X1,,Xi,,Xn),g(X)=g1(X1)gi(Xi)gn(Xn)=b
因此 g i ( X i ’ ) = g i ( X i ) gi(X_i’)=gi(X_i) gi(Xi)=gi(Xi) ,与SG函数的定义矛盾,得证。

下面就让我们看一下 S G 函 数 SG函数 SG 的应用。

例题

有一堆石子共 n n n 个石子,两个人轮流取,每次都只能取 2 的幂次方的个数的石子,取到最后一颗石子的人是胜者。给出你 n n n 的值,请你判断先手是否有必胜策略。
1 ≤ n ≤ 1 0 3 1 \leq n \leq 10^3 1n103
题目来源:HDU 1847(Good Luck in CET-4 Everybody!)

例题代码

//HDU 1847 -- Good Luck in CET-4 Everybody!
#include 
#include 
#define MAXN 1010
#define MAXM 11
using namespace std;
int sg[MAXN], f[MAXM];
bool Hash[MAXN];

void getSG(int m)
{
    memset(sg, 0, sizeof(sg));
    for (int i = 1; i < MAXN; i++)//枚举石子的个数
    {
        memset(Hash, false, sizeof(Hash));
        for (int j = 0; j < m && f[j] <= i; j++)
            Hash[sg[i-f[j]]] = true;//枚举每次拿走的个数并标记 
        for (int j = 0; j < MAXN; j++)
        {
            if (!Hash[j])
            {
            	sg[i] = j;//找到这个F[](该状态可以达到的状态)中不存在的最小的数
            	break;
            }
        }
    }
}

int main()
{
    int n, num = 1;
    for (int i = 0; i < MAXM; num <<= 1, i++)
        f[i] = num;//这里的F数组就是可以移动的步数,每次都是2的幂次
    getSG(MAXM);
    for(int i=1;i<=100;i++)
    {
    	printf("%d ",sg[i]);
	}
	return 0;
    while (cin >> n)
    {
        if (sg[n])
            cout << "Kiki" << endl;
        else
            cout << "Cici" << endl;
    }
    return 0;
}

可以通过 模拟下面的两句话 来理解例题的 代码

  1. KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲S=0 ,则无论先行者如何取子 S → T S→T ST ,都有 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲T≠0
  2. KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲S≠0 ,则先行者必然存在一种取子方法 S → T S→T ST ,且 KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲T=0

此外,求 SG函数 的值还可以用深搜来求,但是没有前面所说的方法那么方便。

  • 求 SG函数的值 的深搜代码
//注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
//n是集合s的大小 S[i]是定义的特殊取法规则的数组
int s[110],sg[10010],n;
int SG_dfs(int x)
{
    int i;
    if(sg[x]!=-1)
        return sg[x];
    bool vis[110];
    memset(vis,0,sizeof(vis));
    for(i=0;i<n;i++)
    {
        if(x>=s[i])
        {
            SG_dfs(x-s[i]);
            vis[sg[x-s[i]]]=1;
        }
    }
    int e;
    for(i=0;;i++)
        if(!vis[i])
        {
            e=i;
            break;
        }
    return sg[x]=e;
}

更多的例题

  • 题目1

有三堆石子共 n , m , p n,m,p n,m,p 个石子,两个人轮流取,每次都只能取斐波那契数列上的个数的石子,取到最后一颗石子的人是胜者。给出你 n , m , p n,m,p n,m,p 的值,请你判断先手是否有必胜策略。
为了方便,我们用 f i f_i fi 来表示斐波那契数列的第 i i i 项,那么有 f 1 = 1 , f 2 = 2 f_1=1,f_2=2 f1=1,f2=2 f i = f i − 1 + f i − 2 ( i ≥ 3 ) f_i=f_{i-1}+f_{i-2}(i \geq 3) fi=fi1+fi2(i3)
共有 T T T 组数据。
1 ≤ n , m , p ≤ 1 0 3 , 1 ≤ T ≤ 1 0 5 1 \leq n,m,p \leq 10^3,1 \leq T \leq 10^5 1n,m,p103,1T105
题目来源:HDU P1848(Fibonacci again and again)

  • 代码1
//HDU 1848 -- Fibonacci again and again
#include 
#include 
int bj[1001],g[1001];
int f[101]={0,1,2};
void sg()
{
	for(int i=1;i<=1000;i++)
	{
		memset(bj,0,sizeof(bj));
		for(int j=1;j<=16;j++)
		{
			if(f[j]>i)
			{
				break;
			}
			bj[g[i-f[j]]]=1;
		}
		for(int j=0;j<=1000;j++)
		{
			if(bj[j]==0)
			{
				g[i]=j;
				break;
			}
		}
	}
}
int main()
{
	for(int i=3;i<=16;i++)
	{
		f[i]=f[i-1]+f[i-2];
	}
	sg();
	while(true)
	{
		int n=0,m=0,p=0;
		scanf("%d %d %d",&n,&m,&p);
		if(n==0 && m==0 && p==0)
		{
			break;
		}
		printf("%s\n",(g[n]^g[m]^g[p])!=0?"Fibo":"Nacci");
	}
	return 0;
}
  • 题目2

有一堆共 n n n 个石子,每次取的个数要在 [ p , q ] [p,q] [p,q] 内,当式子个数不足 p p p 个时要一次将剩下的所有石子取完,取完最后一个石子的人输 ,问你先手是否有必胜策略。
1 ≤ n ≤ 2 16 , 1 ≤ p , q ≤ 2 16 1 \leq n \leq 2^{16},1 \leq p,q \leq 2^{16} 1n216,1p,q216
题目来源:HDU P2897(邂逅明下)

  • 分析

这是一道 打表找规律求SG值 :这类题目可以通过暴力求得小范围的SG值,再通过找规律得到SG值取值的规律来求解。

  • 用于找规律的SG函数代码
#include 
#include 
int bj[1001],g[1001];
int f[101]={0,1,2};
int len=0;
void sg()
{
	for(int i=1;i<=1000;i++)
	{
		memset(bj,0,sizeof(bj));
		if(i<f[1])
		{
			bj[g[0]]=1;
		}
		for(int j=1;j<=len;j++)
		{
			if(f[j]>i)
			{
				break;
			}
			bj[g[i-f[j]]]=1;
		}
		for(int j=0;j<=1000;j++)
		{
			if(bj[j]==0)
			{
				g[i]=j;
				break;
			}
		}
	}
}
int main()
{
	int n=0,p=0,q=0;
	scanf("%d %d %d",&n,&p,&q);
	for(int i=p;i<=q;i++)
	{
		f[++len]=i;
	}
	sg();
	for(int i=1;i<=n;i++)
	{
		printf("%d ",g[i]);
	}
//	if(g[n]!=0)
//	{
//		printf("LOST");
//	}
//	else
//	{
//		printf("WIN");
//	}
	return 0;
}
  • 代码2
//HDOJ 2897 -- 邂逅明下
#include
#include
#include
using namespace std;

int main()
{
    int n,q,p;
    while ( scanf("%d%d%d",&n,&p,&q)!=EOF ) {
        n%=(p+q);
        if ( n>p || n==0 ) printf("WIN\n");
        else printf("LOST\n");
    }
    return 0;
}
  • 题目3

n n n 个点,然后给定这 n n n 个点的拓补图,有向无环。
然后给出你一个正整数 m m m ,表示图上有 m m m 个棋子,这 m m m 个棋子在哪几个点上。
现在有两个玩家来轮流移动这些棋子,谁先没办法移动所有的棋子谁输,问你先手是否有必胜策略。共有 T T T 组测试数据。
1 ≤ T ≤ 1 0 5 , 1 ≤ n ≤ 1000 , 1 ≤ m ≤ 10 1 \leq T \leq 10^5,1 \leq n \leq 1000,1 \leq m \leq 10 1T105,1n1000,1m10
题目来源:POJ P2425(A Chess Game)

  • 代码3
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
 
int map[1010][1010];
int SG[1010];
int N;
 
int DFS(int n)
{
    int i;
    if(SG[n]!=-1) return SG[n];
    bool used[1010];
    memset(used,0,sizeof(used));
    for(i=0; i<N; i++)
    {
        if(map[n][i] != -1)
            used[DFS(i)]=true;
    }
    i=0;
    while(used[i]) i++;
    return SG[n]=i;
}
 
 
int main()
{
    int i,j,k,t;
    int X;
    int tp,ans;
    while(scanf("%d",&N) != EOF)
    {
        memset(map,255,sizeof(map));
        memset(SG,255,sizeof(SG));
        for(i=0; i<N; i++)
        {
            scanf("%d",&k);
            if(k == 0)
            {
                SG[i] = 0;
            }
            for(j=0; j<k; j++)
            {
                scanf("%d",&t);
                map[i][t] = 1;
            }
        }
 
        while(scanf("%d",&X) != EOF)
        {
            if(X == 0) break;
            ans = 0;
            for(i=0; i<X; i++)
            {
                scanf("%d",&tp);
                ans = ans ^DFS(tp);
            }
            if(ans != 0)
                printf("WIN\n");
            else
                printf("LOSE\n");
        }
    }
    return 0;
}

三、取硬币游戏

  • 模型

n n n 枚硬币排成一排,有的正面朝上,有的反面朝上。我们从左开始对硬币按 1 1 1 n n n 编号。
游戏者根据某些约束翻硬币,但他所翻动的硬币中,最右边那个硬币的必须是从正面翻到反面,谁不能翻谁就输,问你先手是否有必胜策略。

  • 约束条件 1 :每次只能翻一个硬币

n n n 枚硬币排成一排,有的正面朝上,有的反面朝上。我们从左开始对硬币按 1 1 1 n n n 编号。游戏者所翻动的硬币必须是从正面翻到反面,谁不能翻谁就输。问你先手是否有必胜策略。

  • 因为这题是只能翻一个硬币,那么这个硬币就是最右边的硬币,所以,每次操作是挑选一个正面的硬币翻成背面。
    对于任意一个正面的硬币,SG值为1。
    奇 数 个 正 面 硬 币 奇数个正面硬币 ,局面的 S G 值 = 1 SG值=1 SG=1 ,先手 必 胜 必胜 ,有 偶 数 个 正 面 硬 币 偶数个正面硬币 ,局面的 S G 值 = 0 SG值=0 SG=0 ,先手 必 败 必败

  • 约束条件 2 :每次能翻转一个或两个硬币(不用连续)

  • 每个硬币的 S G 值 SG值 SG 为它的编号,初始编号为 0 0 0 ,与 N I M 游 戏 NIM游戏 NIM 是一样的。
    如果对于一个局面,把 正 面 硬 币 的 S G 值 异 或 起 来 不 等 于 0 正面硬币的SG值异或起来不等于0 SG0 ,即 a 1 ⊕ a 2 ⊕ a 3 ⊕ … ⊕ a n = x a_1 ⊕ a_2 ⊕ a_3 ⊕ … ⊕ a_n = \text{x} a1a2a3an=x ,对于 a n a_n an 来说一定有 a n ′ = a n ⊕ x < a n a_n' = a_n ⊕ \text{x} < a_n an=anx<an

    如果 a n ′ = 0 a_n'=0 an=0 ,意思就是说,把 a n a_n an 这个值从式子中去掉就可以了。对应游戏,就是把编号为 a n a_n an 的正面硬币翻成背面就可以了。因为 a n ⊕ x = 0 a_n ⊕ \text{x} =0 anx=0,而 a 1 ⊕ a 2 ⊕ a 3 ⊕ … ⊕ a n = x a_1 ⊕ a_2 ⊕ a_3 ⊕ … ⊕ a_n=x a1a2a3an=x ,即 a n ⊕ a 1 ⊕ a 2 ⊕ a 3 ⊕ … ⊕ a n = 0 a_n ⊕ a_1 ⊕ a_2 ⊕ a_3 ⊕ … ⊕ a_n=0 ana1a2a3an=0 ,即 a 1 ⊕ a 2 ⊕ a 3 ⊕ … ⊕ a n − 1 = 0 a_1 ⊕ a_2 ⊕ a_3 ⊕ … ⊕ a_{n-1} = 0 a1a2a3an1=0 ,只要在原来的 x \text{x} x 里面去掉 a n a_n an 就可以了。

    如果 a n ′ ≠ 0 an' \not = 0 an=0 ,意思就是说,把 a n a_n an 这个值从式子中去掉后再在式子中加上 a n ′ a_n' an a n ′ < a n a_n' < a_n an<an 。对应游戏,去掉 a n a_n an 就是把编号为 a n a_n an 的正面硬币翻成背面,加上 a n ′ a_n' an ,如果编号为 a n ′ a_n' an 的硬币是正面,我们就把它翻成背面,是背面就翻成正面,总之,就是翻转编号为 a n ′ a_n' an 的硬币。因为 a n ⊕ x ≠ 0 an ⊕ x \not= 0 anx=0 ,所以 a n ⊕ a 1 ⊕ a 2 ⊕ a 3 ⊕ … ⊕ a n ≠ 0 a_n ⊕ a_1 ⊕ a_2 ⊕ a_3 ⊕ … ⊕ a_n \not= 0 ana1a2a3an=0 ,即 a 1 ⊕ a 2 ⊕ a 3 ⊕ … ⊕ a n − 1 ≠ 0 a_1 ⊕ a_2 ⊕ a_3 ⊕ … ⊕ a_{n-1} \not= 0 a1a2a3an1=0 ,而这里的 a n ′ = a 1 ⊕ a 2 ⊕ a 3 ⊕ … ⊕ a n − 1 a_n'=a_1 ⊕ a_2 ⊕ a_3 ⊕ … ⊕ a_{n-1} an=a1a2a3an1 ,所以在 x x x 中去掉 a n a_n an 后,要对 a n ′ a_n' an 进行异或,也就是翻转,正转反,反转正。

  • 约束条件 3 :每次必须连续翻转 k 个硬币

    我们以 k = 3 k=3 k=3 为例。

    我们计算的是个数为 n n n 的硬币中,其中最后一个硬币为正面朝上,的 s g 值 sg值 sg

    n = 1 n=1 n=1 时,硬币为:正,先手必输,所以 s g [ 1 ] = 0 sg[1]=0 sg[1]=0

    n = 2 n=2 n=2 时,硬币为:反正,先手必输,所以 s g [ 2 ] = 0 sg[2]=0 sg[2]=0

    n = 3 n=3 n=3 时,硬币为:反反正,先手必胜,所以 s g [ 3 ] = 1 sg[3]=1 sg[3]=1

    n = 4 n=4 n=4 时,硬币为:反反反正,先手操作后为:反正正反,子状态局面的 S G = 0 ⊕ 1 = 1 SG=0 ⊕ 1=1 SG=01=1 ,那么 s g [ 4 ] = 0 sg[4]=0 sg[4]=0

    n = 5 n=5 n=5 时,硬币为:反反反反正,先手操作后为:反反正正反,子状态局面的 S G = 1 ⊕ 0 = 1 SG=1 ⊕ 0 = 1 SG=10=1 ,那么 s g [ 5 ] = 0 sg[5] = 0 sg[5]=0

    n = 6 n=6 n=6 时,硬币为:反反反反反正,先手操作后为:反反反正正反,子状态局面的 S G = 0 ⊕ 0 = 0 SG=0 ⊕ 0 = 0 SG=00=0 ,那么 s g [ 6 ] = 1 sg[6] = 1 sg[6]=1

    然后我们得到了下面的这个表格:

n 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
sg值 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1

由此我们可以知道,从编号为 1 1 1 开始, s g sg sg 值为: 001001001001...... 001 001 001 001...... 001001001001......

通过继续观察,我们可以知道, s g sg sg 的形式为 000 … 01000 … 01 000…01000…01 0000100001 ,其中每一段 0 0 0 的个数都为 ( k − 1 ) (k-1) (k1)

  • 约束条件 4 :每次翻动一个硬币后,必须翻动其左侧最近三个硬币中的一个,即翻动第 x 个硬币后,必须选择 (x-1) 、 (x-2) 、 (x-3) 中的其中一个硬币进行翻动,除非 x 是小于等于 3 的 (Subtraction Games)

    n = 1 n=1 n=1 时,硬币为:正,先手必赢,所以 s g [ 1 ] = 1 sg[1]=1 sg[1]=1

    n = 2 n=2 n=2 时,硬币为:反正,先手必赢,因为先手可以翻成反反或正反,可能性为 2 2 2 ,所以 s g [ 2 ] = 2 sg[2]=2 sg[2]=2

    然后我们得到了下面的这个表格:

n 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
sg值 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3

这个与每次最多只能取 3 3 3 个石子的取石子游戏的 S G SG SG 分布一样,同样还有相似的这类游戏, 约 束 条 件 5 约束条件5 5 也是一样。

  • 约束条件 5 :每次必须翻动两个硬币,而且这两个硬币的距离要在可行集 S={1,2,3} 中,硬币序号从 0 开始 (Twins游戏)

n = 1 n=1 n=1 时,硬币为:正,先手必输,所以 s g [ 0 ] = 0 sg[0]=0 sg[0]=0

n = 2 n=2 n=2 时,硬币为:反正,先手必赢,所以 s g [ 1 ] = 1 sg[1]=1 sg[1]=1

n = 3 n=3 n=3 时,硬币为:反反正,先手必赢,所以 s g [ 2 ] = 2 sg[2]=2 sg[2]=2

n = 4 n=4 n=4 时,硬币为:反反反正,先手必赢,所以 s g [ 3 ] = 3 sg[3]=3 sg[3]=3

n = 5 n=5 n=5 时,硬币为:反反反反正,先手必输,所以 s g [ 4 ] = 0 sg[4]=0 sg[4]=0

然后我们得到了下面的这个表格:

n 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
sg值 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3
  • 约束条件6:每次可以翻动一个、二个或三个硬币 (Mock Turtles游戏)

初始编号从 0 0 0 开始。

n 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
sg值 1 2 4 7 8 11 13 14 16 19 21 22 25 26 28 31

看上去 s g 值 sg值 sg 2 x 2 \text{x} 2x 或者 ( 2 x + 1 ) (2 \text{x} +1) (2x+1) 。我们称一个非负整数为 o d i o u s odious odious ,当且仅当该数的二进制形式的 1 1 1 出现的次数是奇数,否则称作 e v i l evil evil 。所以 1 1 1 2 2 2 4 4 4 7 7 7 o d i o u s odious odious 因为它们的二进制形式是 1 , 10 , 100 , 111 1,10,100,111 1,10,100,111 。而 0 , 3 , 5 , 6 0,3,5,6 0,3,5,6 e v i l evil evil ,因为它们的二进制形式是 0 , 11 , 101 , 110 0,11,101,110 0,11,101,110 。而上面那个表中,貌似 s g 值 sg值 sg 都是 o d i o u s 数 odious数 odious

所以当 2 x 2 \text{x} 2x 为 odious 时, sg值 是 2 x 2 \text{x} 2x,当 2 x 2 \text{x} 2x 是 evil 时, sg值 是 ( 2 x + 1 ) (2 \text{x} +1) (2x+1)

  • 对于约束条件 7、8 有兴趣的读者可以在 “参考资料 [9]” 中继续阅读

参考资料

[1] 由感性认识到理性认识——透析一类搏弈游戏的解答过程。

[2] 组合游戏略述——浅谈SG游戏的若干拓展及变形。

[3] 博弈论。

[4] SG函数。

[5] hdu 1848Fibonacci again and again。

[6] 专题训练之博弈。

[7] 博弈by高嘉煊。

[8] sg函数入门题。

[9] 【转】博弈-翻硬币游戏。

[10] 博弈:关于SG函数的一些心得(知识总结+叙述证明+例题)。

[11] POJ2425(树形,无向无环图博弈)。

PS:

由于有的参考资料因为“ABlog”网站的关闭而暂时无法提供出来,我会尽力将文件找回的!

你可能感兴趣的:(算法学习)