今天無意中再群里看到有人在討論計算組合數的一種方法
感覺還有點用就記錄下來了 稍稍的證明了下 證明不是很嚴密,看官湊合著看吧
c(n,m) = n!/(m!*(n-m)!)
如果n和m比較大時 可能會導致中間超過數據范圍,不過我們可以直接用遞推也就是
c(n,m) = c(n-1,m)+c(n-1,m-1)
不過下面我們要用另外一種方法來計算這個組合
把分子和分母約掉之后就變成了
(n*(n-1)*……*(n-m+1))/m!
這樣上面和下面都是m個元素,我們可能會想到乘1個除1個,這樣來避免越界,可是這樣會剛好整除嗎?不會出現小數使得最后的答案變錯嗎?其實只要我們按照一定的順序來乘和除的話,是不會產生小數的,也就是說會一直整除的.下面給出我的簡單證明,如有錯誤,還請指出(這個公式是借的這位博主的)
首先我們把上面那個分數寫成這樣的
(n*(n-1)*……*(n-m+1))/(m*(m-1)*……*1)
這樣的話就相當于n–>m (n-1)—>(m-1) …… (n-m+1)—>1
下面先給出主要程序吧
f = 1;//最后答案
for(i = 1;i <= m;i++)
f = f*(i+(n-m))/i;
現在主要的是要說明每次乘以(i+(n-m))然后再除以i不會產生小數,我是這么想的,說先i是1,肯定不會有小數,然后i=2,但是分母已經乘過兩個數了,肯定有一個奇數,一個偶數,也不會產生小數,同理3的時候也不會,但是4的時候可能會這樣想,前面某個數k整除4,那已經別2除掉了一個2,這樣會不會產生小數呢?結果是不會的,因為4個相當于2個2,或者1個4,我們可以把除2的那個選擇到這4個當中不能出4的那一個數上,這樣的話,就剩下了一個能整除4的了,然后其他的就和這里一樣了。
Fibonacci Nim是如下一種游戲
一堆石子有n顆.兩人按如下規則輪流取一定的石子。
1.第一個取的至少取1顆,至多取n-1顆
2.每次取的石子數不能超過對手剛取的2倍,最后取完的算贏家。
現在我們需要算出對于某個n,是先手必勝,還是后手必勝。看到題目,基本也就想到一二了,肯定和Fibonacci數列有關,確實。不過怎么個有關法呢?相信只要你動手算幾個小數,就會猜出來了,對于n = Fi先手一定必敗,否則先手必勝。好了,下面我們就來證明這個結論吧:
首先我們會用到如下三個性質:
I.若K >= N,則狀態(N,K)必勝(在這里我們用(N,K)表示還剩下N顆石子,最多能取K顆石子的一個狀態)
II.若狀態(N,N-1)先手必敗,那么狀態(N,K)(K<N)必敗。
III.若狀態(N,K)(K<N)則最后一次取走的石子數不超過2*N/3
下面證明(Fi,K)(K<Fi)必敗
一.F1(=2),F2(=3)顯然成立.
二.若F1至Fi成立,則F(i+1)成立
設先手取K顆石子。
1).若K>=F(i-1),后手得到狀態(N-K,2*K)(N=F(i+1)),2*K>=2*F(i-1)>F(i-1)+F(i-2)=F(i)>N-K.所以后手必勝,也就是先手必敗。
2).若K <= F(i-1)
我們可知(F(i-1),K)必敗(假設得到的),所以后手可以使先手達到(F(i),X)(X < F(i))狀態
由性質III可得X <= (2*F(i-1)/3)*2 = 4*F(i-1)/3 = F(i-1)+1/2*F(i-1) <= F(i-1)+F(i-2)=F(i),所以(F(i),X)必敗。
下面是n != F(i)
那么(N,N-1)先手必勝。這要使得后手處于<=n的最大的Fibonacci數就行,這樣就相當于后手必輸,也就是先手必勝。
Dynamic subtraction.
One can enlarge the class of subtraction games by letting the subtraction set depend on the last move of the opponent.Many early example sappear in Chapter12 of Schuh(1968).Here are two other examples.(For a generalization,see Schwenk(1970).) (a)There is one pile of n chips.The ?rst player to move may remove as many chips as desired,at least one chip but not the whole pile.There after,the players alternate moving,each player not being allowed to remove more chips than his opponent took on the previous move.What is an optimal move for the ?rst player if n =44?For what values of n does the second player have a win?
題目大意: 有一堆石子,個數為n,兩個人輪流,規則如下 第一個取石子的人至少取一個,至多取n-1個。之后每個人不能比前一個人剛取過的石子數多.沒得取了算輸,
對于這個我們對比較小的n歸納可知,如果n=2^k(k >=0)的話則是P態,否則是N態. 首先我們可以看到1是P態(先手無法取,因為不能取完),2是P態(只能取1個,后手取剩下的一個) 所有的奇數都是N態,因為每次取一個的話,最后只剩下1顆石子的時候一定是由先手取,所以所有的奇數是N態. 如果是偶數的話,第一次取的石子數不能超過一半,而且必須取偶數.如果取奇數個的話,則后手變成了N態,如果取超過一半的石子數的話,那么后手可以一次取完.先手也輸了. 所以4是P態 6只能取2,然后后手達到4這個P態,所以先手必輸。 下面我們證明當n = 2^k時為P態, 首先i = 0,1,2時結論成立。現在假設n = 2^i(i>=1)時結論成立,令j = i+1;m = 2^j; 我們知道先手第一次取的石子數一定是小于2^i次的,而且從2^j到2^i和2^i到2^j是一樣多的數目. 于是我們可以看成先手可不可能通過取走一些石子使得后手處于2^i.其實這樣是不可能的,分析如下: 我們把2^j到2^i這些數都同時減去2^i,我們得到2^i,2^i-1,……,1,0,這樣就變成了一個2^i的取石子游戲了.我們可以知道在這里先手是必輸的(本文如沒有特殊說明,則意味著兩個選手都按最優的方案執行)。首先我們得到2^(i-1)次,如果先手取的石子數比這個多的話,那么后手可以取走一定的數目使得先手處于2^i這個P態.也就是說先手必輸,如果先手取走的數目比2^(i-1)少的話,那么可以得到取完2^j到2^i+1這個2^i個的時候先手也是必輸的,也就是說在這個過程中后手同樣可以使得先手處于2^i這個P態,這樣的話先手必輸,所以無論先手怎樣取石子,對于n=2^k(k>=0)必輸。 這樣的話,易知對于n != 2^k(k>=0)的先手必勝。
下面我們以一種游戲的方式來引進三種基本的博弈問題。
一.巴什博奕(Bash Game):
首先我們來玩一個比較古老的報數游戲。A和B一起報數,每個人每次最少報一個,最多報4個。輪流報數,看誰先報到30.
如果不知道巴什博弈的可能會覺得這個是個有運氣成分的問題,但是如果知道的人一定知道怎樣一定可以贏。
比如A先報數的話,那么B一定可以贏(這里假定B知道怎么正確的報數)
B可以這樣報數,每次報5-k(A)個數,其中k(A)是A報數的個數這樣的話沒一次
兩人報完數之后會變成5 10 15 20 25 30這樣是不是B一定會贏呢?是不是有一種被欺騙的感覺呢?好吧下面我們來看看這個原理。我們先看下一個一眼就能看出答案的例子 比如說我們報到5(4+1),每次報最多報4個,最少報1個.那么是不是后者一定可以贏呢?答案是肯定的。好了到這巴什博弈的精髓基本就OK了。
那么如果我們要報到n+1,每次最多報n個,最少報1個的話,后者一定能夠贏。
現在我們需要報數到n,而每次最多報數m個,最少報數1個.我們可以化成這樣
n = k*(1+m)+r(0 <= r <= m)這樣的話如果r不等于0那么先手一定會贏,為什么呢?首先先手報r個,那么剩下k倍(1+m)個數,那么我們每次報數1+m-k(B)個數就一定能保證最后剩下1+m個,那么就到了上面我們說的那個了,先手就一定會贏,如果r=0那么后手一定會贏,道理一樣的。
到這巴什博弈也就介紹完了,知道這個道理之后我們也可以去騙小朋友了。-_-//
二.威佐夫博奕(Wythoff Game):
這種博弈比前面一種要稍微復雜一點。我們來看下下面這個游戲。
有兩堆火柴棍,每次可以從某一堆取至少1根火柴棍(無上限),或者從兩堆取相同的火柴棍數。最后取完的是勝利者。好了,如果你不知道這個博弈定理,對于小數目的火柴棍數,可能還能推出來,但是如果火柴棍數一多,就不行了。看了下面的這個介紹,你也會有一種被騙的感覺。
首先我們知道兩堆火柴是沒有差別的,也就是說第一堆有a根,第二堆有b根和第一堆有b根,第二堆有a根是一樣的結果。
我們用一個二維的狀態(a,b)來記錄當前剩下的火柴數,表示第一堆剩下a根火柴,第二堆剩下b根火柴。同樣我們假設兩個人的編號是A和B,且A先取。
那么如果某個人遇到了這樣的狀態(0,0)那么也就是說這個人輸了。這樣的狀態我們叫做奇異狀態,也可以叫做失敗態。
那么接下來的幾個失敗態為(1,2),(3,5),(4,7),(6,10),(8,13)……
我們用a[i]表示失敗態中的第一個,b[i]表示失敗態中的第二個.(i從0開始).
那么我們可以看到b[i] = a[i]+i;(i >= 0),a[i]是前面的失敗態中沒有出現過的最小的整數
下面我們可以得到三個基本的結論。
1.每個數僅包含在一個失敗態中
首先我們知道a[k]是不可能和前面的失敗態中的a[i],b[i]重復的(這點由a[i]的得到可以知道)
b[k] = a[k]+k > a[k-1]+k>a[k-1]+k-1+1>a[k-1]+(k-1) = b[k-1]>a[k-1]這樣我們知道每個數僅在一個失敗態中。
2.每個失敗態可以轉到非失敗態。
加入當前的失敗態為(a,b),那么如果我們只在一堆中取的話,肯定會變成非失敗態(這點由第一點可以保證),如果從兩堆同時取的話,由于每個失敗態的差是不一樣的,所以也不可能得到一個失敗態。也就是說一個失敗態不管你怎么取,都會得到一個非失敗態。
3.每個非失敗態都可以轉到一個失敗態
對于這個結論,首先我們要知到每個狀態(a,b)要么a = a[i],要么b = b[i].(每個數都出現在一個失敗態中),下面我們分兩種情況來討論
I.a = a[i].如果b = a的話那么一次取完就變成了(0,0).如果b > b[i]的話,那么我們從第二堆中取走b-b[i]就變成了一個失敗態。如果b < b[i].那么我們從兩堆中同時取走a-a[b-a[i]]這樣得到失敗態(a[b-a[i]],a[b-a[i]]+b-a[i])(a[i] = a)
II.b = b[i].如果a > a[i]那么我們從第一堆中取走a-a[i]根火柴.
如果a < a[i].這里又分兩種情況。第一是a = a[k](k < i)
那么我們從第二堆取走b - b[k]就行了。
第二是a = b[k]這樣的話由于兩堆火柴是沒有區別的,所以我們把b變成a[k]就行了,也即是從第二堆火柴中取走b - a[k]就變成了失敗態
至于怎么判斷一個狀態是否是失敗態.我們可以用下面的方法來判斷(本人暫時還不會證明)
a[i] = [i*(1+√5)/2](這里的中括號表示向下取整) b[i] = a[i]+i;
那么這就是一個失敗態,
看了這之后可以去找POJ1067練練手
三.尼姆博奕(Nimm Game):
這個已經變成了三堆火柴了。每次只能從某一堆取任意個(至少為1),最后取完的為勝利者。
這個博弈我們用三維的狀態來表示(a,b,c).對于每個失敗態我們有a^b^c = 0至于為什么我暫時不會證(記得陳景潤的一本組合數學中有證明,后面要是懂了再來補吧)
對于一個非失敗態我們可以通過轉換得到一個失敗態,也就是說(a,b,c)我們可以通過如下的操作得到一個失敗態,如果a^b < c那么我們從第三堆中取走c-a^b根,如果a^c < b那么我們從第二堆中取走b - a^c根.如果b^c < a那么我們從第一堆中取走a - b^c根。這樣就變成了一個失敗態。
由于水平有限,暫時只能寫這么多了。
這個題是二維的格雷碼,把兩個合并起來。
題意是把0到2^(n +m)-1的數寫成2^n * 2^m的矩陣,使得位置相鄰兩數的二進制表示只有一位之差
這樣的話就是把n位的格雷碼和m位的格雷碼合并起來就行了
拿n = 1 m = 2來說吧
可以自己先窮舉一下會發現時下面的樣子
一
|
二
|
三
|
四
|
000(0) |
100(4) |
110(6) |
010(2) |
001(1) |
101(5) |
111(7) |
011(3) |
可以發現各列的前兩位是一樣的,也是m位的gray(其實先把所有列合并起來,然后再在后面加上n的gray就成了這個了)
這樣的話,實現起來就比較容易了
代碼如下(第n個gray碼是n ^ (n >> 1)(其中n從0開始))