演算法與資料結構 博弈論(高階篇之尼姆博弈)

2021-10-02 22:14:04 字數 3867 閱讀 5476

尼姆博弈(nimm game):

有任意堆物品,每堆物品的個數是任意的,雙方輪流從中取物品,每一次只能從一堆物品中取部分或全部物品,最少取一件,取到最後一件物品的人獲勝。

分析:我們先來看假設有三堆物品時的情況

這種情況最有意思,它與二進位制有密切關係,我們用(a,b,c)表示某種局勢,則前幾種奇異局勢如下:

第一種是(0,0,0),此時無論是誰面對該局勢,都必敗

第二種是(0,n,n),此時只要與對手拿走一樣多的物品,最後都將導致(0,0,0)出現。比如對手在這種情況下,其於第二堆物品中先取走k個物品,即局勢變為(0,n-k,n),那麼輪到我們的時候,我們就在第三堆中也取走k個物品,即局勢變為(0,n-k,n-k)。這樣不斷重複下去,最終對手總會先把某堆物品取光,那麼等到我的回合時我再將剩餘那一堆的物品取光。於是最終出現奇異局勢(0,0,0)給對方

接下來分析一下當三堆物品均不為0時的第乙個奇異局勢:(1,2,3),此時無論先手怎麼拿,接下來對手都可以將其變為(0,n,n)的情形,從而使得對方輸掉比賽。比如:

若先手在第一堆中取走1個物品,即局勢變為(0,2,3),那麼後手就在第三堆中取走1個物品,使局勢變為(0,2,2)。此時出現局勢(0,n,n)給對方;

若先手在第二堆中取走1個物品,即局勢變為(1,1,3),那麼後手就在第三堆中取走3個物品,使局勢變為(1,1,0)。此時出現局勢(n,n,0)給對方;

若先手在第二堆中取走2個物品,即局勢變為(1,0,3),那麼後手就在第三堆中取走2個物品,使局勢變為(1,0,1)。此時出現局勢(n,0,n)給對方;

若先手在第三堆中取走1個物品,即局勢變為(1,2,2),那麼後手就在第一堆中取走1個物品,使局勢變為(0,2,2)。此時出現局勢(0,n,n)給對方;

若先手在第三堆中取走2個物品,即局勢變為(1,2,1),那麼後手就在第二堆中取走2個物品,使局勢變為(1,0,1)。此時出現局勢(n,0,n)給對方;

若先手在第三堆中取走3個物品,即局勢變為(1,2,0),那麼後手就在第二堆中取走1個物品,使局勢變為(1,1,0)。此時出現局勢(n,n,0)給對方;

在離散數學中有一種邏輯運算叫做「異或」,其算術符號為「⊕」

這個運算的法則是「相同為0,不同為1」,即1⊕1=0,0⊕0=0,而1⊕0=1,0⊕1=1

我們來看一下奇異局勢(1,2,3)在進行異或運算之後的結果:

首先先將1(二進位制為01)和2(二進位制為10)進行異或運算:

0110

——11

然後將得到的結果再與3(二進位制為11)進行異或運算:

1111

——00

發現最終的結果為0

對於奇異局勢(0,n,n)也一樣,結果也是0

實際上,我們不難發現,對於任何奇異局勢(a,b,c)都有a⊕b⊕c = 0

因此對於某個給出的局勢(a,b,c),我們判斷其是否為奇異局勢的方法就是判斷a⊕b⊕c是否為0

注意到對於任何數a,都有a⊕a=0

且根據異或運算的結合律和交換律我們不難得到 a⊕b⊕(a⊕b)=(a⊕a)⊕(b⊕b)=0⊕0=0

所以從乙個非奇異局勢向乙個奇異局勢轉換的方式可以是:

① 使 a = c⊕b

② 使 b = a⊕c

③ 使 c = a⊕b

比如說對於某個局勢(14,21,39)

由於14⊕21=27,而39-27=12,所以從39中拿走12個物體即可達到奇異局勢(14,21,27)

又比如說對於局勢(55,81,121)

由於55⊕81=102,而121-102=19,所以從121中拿走19個物品就形成了奇異局勢(55,81,102)

總結:以上談論的全都是關於n=3時的情況,而實際上,我們把n(n≥3)推廣,對於上述的推理依然是成立的

即,判斷某個局勢(a1,a2,……,an)是否為奇異局勢,我們的方法是將a1⊕a2⊕……⊕an

若得到的結果為0則說明其為乙個奇異局勢,否則不是

而將乙個非奇異局勢轉換為乙個奇異局勢轉換的方法可以是:

(1) 使 a1 = a2⊕……⊕an

……(i) 使 ai = a1⊕……⊕a(i-1)⊕a(i+1)……⊕an

……(n) 使 an = a1⊕……⊕an-1

—— 經典題型 ——

hdu2176 取(m堆)石子遊戲

問題描述

m堆石子,兩人輪流取,只能在1堆中取,取完者勝。先取者負輸出no,先取者勝輸出yes,然後輸出怎樣取子。例如5堆 5,7,8,9,10先取者勝,先取者第1次取時可以從有8個的那一堆取走7個剩下1個,也可以從有9個的中那一堆取走9個剩下0個,也可以從有10個的中那一堆取走7個剩下3個。

輸入資料

輸入有多組。每組第1行是m(m<=200000),後面m個非零正整數,m=0時退出。

輸出資料

先取者負輸出no,先取者勝輸出yes,然後輸出先取者第1次取子的所有方法。如果從有a個石子的堆中取若干個後剩下b個後會勝就輸出a b。參看樣例輸出

樣例輸入

245 45

33 6 9

55 7 8 9 10

0樣例輸出

noyes

9 5yes

8 19 0

10 3

分析:這道題是實際就是尼姆博弈

題目的要求是,對於其輸入的某個局勢先判斷其是否為奇異局勢

是則表示先手必輸,輸出」no」;反之則先手必勝,輸出」yes」

對於非奇異局勢,還要求你將所有的能讓該非奇異局勢轉換為奇異局勢的方案都輸出

比如對於局勢(3,6,9),由於該局勢為非奇異局勢,故先手必勝,因此需要先輸出」yes」

接著在這個局勢中,發現只有將9變為5才能使得該非奇異局勢變為奇異局勢(3,6,5)

故還需要再在下一行輸出」9 5」

顯然,本題的難點也正是在這個輸出取子方案上

首先要知道,判斷某個局勢(a1,a2,……,an)是非為奇異局勢的方法是將a1⊕a2⊕……⊕an

如果得到的結果為0就說明該局勢是奇異局勢

而轉換方案是:

(1) 使 a1 = a2⊕……⊕an

……(i) 使 ai = a1⊕……⊕ai-1⊕ai+1……⊕an

……(n) 使 an = a1⊕……⊕an-1

那麼我們在判斷了某個局勢是否奇異之後,就可以通過列舉的方式來把上面的 a1-an都求出來,從而得到某個ai應該剩下多少才能形成乙個奇異局勢

但是需要注意的是,並不是對於每個求出的ai都是可以的

比如說對於局勢(3,6,9),可以很容易算出a1= a2⊕a3 = 6⊕9 = 15

但是15>3,我們顯然無法將第一堆中的物品(3個)拿成15個,因此我們還需要對每個列舉出的結果進行判斷,而判斷的正是a1⊕……⊕ai-1⊕ai+1……⊕an是否小於初始局勢中的對應的ai

對於計算ai,這裡也有個技巧。我們不必在每次的列舉中都計算a1⊕……⊕ai-1⊕ai+1……⊕an,這樣顯然極易超時。實際上,我們在一開始就把a1⊕……⊕an的結果放進某個變數var中,在之後求某個ai時,僅僅只需要用var⊕ai就可以了(根據異或運算的性質:n⊕n=0,n⊕0=n):

var ⊕ ai = (a1⊕……⊕ai-1⊕ai⊕ai+1⊕……⊕an ) ⊕ ai

= a1⊕……⊕ai-1⊕ (ai⊕ai) ⊕ai+1⊕……⊕an

= a1⊕……⊕ai-1⊕ 0 ⊕ai+1⊕……⊕an

= a1⊕……⊕ai-1⊕ai+1⊕……⊕an = ai

下面直接給出本題的完整**:

#include

using

namespace std;

const

int max=

200010

;int ary[max]

;int

main()

if(var==

0) cout<<

"no"

return0;

}

演算法與資料結構 博弈論(高階篇之SG博弈)

sg博弈的命名源於sg函式和sg定理,而sg函式的出現則來自於乙個簡單的取石子遊戲 有1堆n個的石子,每次只能取個石子,先取完石子者勝利,判斷對於不同的n,先手能否取勝?分析 這個遊戲和巴什博弈的不同在於 sg博弈中取東西是無規則不連續的,而巴什博弈中取東西則是連續的 因此在討論sg博弈時要複雜很多...

演算法與資料結構 博弈論

遊戲a 簡單博弈 有兩個遊戲者 a和b。有21顆石子。兩人輪流取走石子,每次可取1 2或3顆。a先取。取走最後一顆石子的人獲勝,即沒有石子可取的人算輸。如果剩下1 2或3顆石子,那麼接下來取的人就能獲勝 如果剩下4顆,那麼無論接下來的人怎麼取,都會出現前面這種情況,所以接下來取的人一定會輸 如果剩下...

資料結構與演算法之小白高階

作為乙個馬上要面對實習的大學生,我深知自己的知識水平和能力的有限,所以準備從現在開始主攻資料結構與演算法,雖然之前也有過接觸,但是沒有系統 全面的了解過,作為面試與工作中必備的重要技能,學好資料結構與演算法是非常重要的。所以我會與大家一起分享每天學習的經驗所得,希望每乙個奮鬥在這條路上的朋友都會達到...