尼姆博弈(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顆,那麼無論接下來的人怎麼取,都會出現前面這種情況,所以接下來取的人一定會輸 如果剩下...
資料結構與演算法之小白高階
作為乙個馬上要面對實習的大學生,我深知自己的知識水平和能力的有限,所以準備從現在開始主攻資料結構與演算法,雖然之前也有過接觸,但是沒有系統 全面的了解過,作為面試與工作中必備的重要技能,學好資料結構與演算法是非常重要的。所以我會與大家一起分享每天學習的經驗所得,希望每乙個奮鬥在這條路上的朋友都會達到...