動態規劃問題之求組合數

2021-08-20 05:53:45 字數 1433 閱讀 1157

我們知道,組合數在組合數學中非常有用,組合數是乙個十分常用的數字。

比如,書架上有4本書abcd,我們想拿2本讀,那麼有c(4,2)種方法。

這個數字的求法是:4!/2!(4-2)!,其中n!=1*2*3...n,是階乘數。

所以按定義求其實也十分「容易」,這裡是指容易寫罷了。

先求乙個階乘:

def fac(n):

if n<=1:

return 1

s=1while n>1:

s*=n

n-=1

return s

然後求組合數cnm:

def cnm(n,m):

if n<0 or m <0 or n真的是非常容易理解了。

數字小,還好說。但如果你不用python,你的階乘演算法很可能溢位,(或者一些新手用遞迴寫階乘,python是不能高階遞迴的,大概990+次遞迴),那麼這個就很麻煩了。

實際上高中老師教的計算方法是,c(n,k)=n*(n-1)...(n-k)/k*(k-1)...1;

原則上是簡化了「手算」。

但是不能根本上保證階乘溢位問題。

苦苦思索,高中真的白讀了嗎?

其實真的不是,有一種結合了高中一道經典組合題產生的動態規劃法可以相對較快的求出組合數,只是多花了不少空間。

考慮:乙個4x5的方格組,也就是一行5個格仔,有4行,最開始你站在最左上角,你可以向下或向右一步,問有幾種方法來到達最右下角。

高中數學解法是這樣的,你一共需要走7步,但是你需要選擇3步向下,4步向右,但是選擇在什麼時候來走下,其餘走右,

這就是c(7,3)的數字。

哇,這東西出來組合數了誒。

但是我們考慮乙個**,每個格對應方格祖的乙個,這個格仔記錄你到達這裡有幾種方法。

動態規劃的思想是:你為了到達a[i][j],你可以從這個格上面的格仔向下走,也可以選擇從左邊的格仔向右走。

這是乙個『或』事件思想。也就是a[i][j]=a[i-1][j]+a[i][j-1]

為此我們為了初始化乙個可以求解的**,在最外圈加一層都是『1』的格仔,其實很好理解,到達a【k】只有一種方法,那就是一直向右走,縱列同理。

所以我們最後的結果是a[n][n].

def cnm(n,k):

#為了初始化簡單,直接全部設為1

dp=[[1 for x in range(k+1)] for y in range(n-k+1)]

for i in range(1,n-k+1):

for j in range(1,k+1):

dp[i][j]=dp[i][j-1]+dp[i-1][j]

return dp[-1][-1]

其中返回的是要求的數字,但是實際上我們算出了所有組合,而且斜對角看是乙個楊輝三角。

這用了o(nk)的時間與空間,但是換來了更快的、不溢位的算式。

問題 B 求組合數

題目鏈結 題目描述 組合數的計算雖說簡單但也不乏有些陷阱,這主要是因為語言中的資料型別在表示範圍上是有限的。更何況還有中間結果溢位的現象,所以千萬要小心。輸入 求組合數的資料都是成對 m與n 出現的,每對整數m和n滿足0 m,n 20,以eof結束。輸出 輸出該組合數。每個組合數換行。樣例輸入 5 ...

組合數學 求組合數

對於求組合數,要根據所給資料範圍來選擇合適的演算法 這道題中所給的資料範圍適合用打表的方法直接暴力求解 先用4e6的複雜度預處理出所有的情況,再用1e4的複雜度完成詢問即可 include using namespace std const int n 2010 const int mod 1e9 ...

動態規劃之硬幣組合問題

問題 如果我們有面值為1元 3元和5元的硬幣若干枚,如何用最少的硬幣湊夠11元?動態規劃的本質是將原問題分解為同性質的若干相同子結構,在求解最優值的過程中將子結構的最優值記錄到乙個表中以避免有時會有大量的重複計算。例如硬幣組合問題,若求湊夠11元的最少硬幣數,可以先從湊夠0元 1元 2元 的子結構開...