多重集組合數,有\(n\)種物品,第 i 種有\(a_i\)個。不同種類的物品可以互相區分但是相同的種類的無法區別。從這些物品中取\(m\)個有多少種取法。這是乙個比較經典的計數dp問題,我們設限制條件:
\(1 \leq n \leq 1000\)
\(1 \leq m \leq 1000\)
\(1 \leq a_i \leq 1000\)
\(2 \leq m \leq 10000\)
dp[i+1][j] := 從前i種物品中取出j個的組合總數。
暴力法思維量較小,應用遞推。我們可以把從前\(i\)種物品取\(j\)個,劃分為:從前\(i - 1\)種物品中取\(j-k\)個,從剩下的第\(i\)種物品裡取剩下的\(k\)個。則遞推式可以表示為:
\[dp[i+1][j] = \sum_^}dp[i][j-k]
\]但是利用這個遞推式進行計算的時間複雜度為\(\mathcal\),根據限制條件,該演算法時間複雜度過高,需要進行優化。
我們對遞推式進行優化,優化後的結果如下,下面將闡明如何得到這個遞推式的:
\[dp[i+1][j] = dp[i + 1][j - 1] + dp[i][j] - dp[i][j - 1 - a_i]
\]分兩種情況:
這時候\(dp[i + 1][j - 1]\)的k的取值範圍是\([0, j - 1]\):
\[dp[i + 1][j - 1] = dp[i][j-1] + dp[i][j-2] + \cdots + dp[i][1] + dp[i][0]
\]又因為\(a[i] > j - 1 \rightarrow a[i] \ge j\),這時k的取值範圍是\([0, j]\):
\[dp[i+1][j] = dp[i][j] + dp[i][j - 1] + \cdots + dp[i][1] + dp[i][0]
\]可以發現\(dp[i+1][j]與dp[i+1][j-1]\)之間只差乙個\(dp[i][j]\)可以用如下圖表示:
這時,將式子進行展開得到:
\[\left\
xdp[i+1][j] = dp[i][j] + dp[i][j-1] + \cdots + dp[i][j-a[i]] \\
dp[i+1][j-1] = dp[i][j-1] + \cdots + dp[i][j-1-a[i]] \\
\end\right.
\]用影象進行表示如下:
因此,可以證明上述表示式成立。
核心**如下:
int n, m;
int a[max_n]
int dp[max_n][max_m]
void solve()else dp[i + 1][j] = dp[i + 1][j - 1] + dp[i][j];
} }}
經典例題:poj3046 - ant countin 多重組合數 計數類DP
題目 有n種物品,第i種物品有p i 個,不同種類的物品可以相互區分,同種類的物品不能相互區分。從這些物品種取出m個,有多少種取法,答案對mod取模。思想 dp i j 表示前i種物品,一共拿了j個物品的方案數。為了得到dp i j 那麼可以從前i 1種物品取j k個,再從第i種物品取k個即可 下面...
計數dp 劃分數 多重集組合數
劃分數 把n個無區別的物品劃分成不超過m組。dp i j j的i劃分的總數。dp i j dp i j i dp i 1 j 即 將j個物品分成i份,有兩種情況 每份劃分都大於等於1 dp i j i 存在有乙份以上用0劃分dp i 1 j int main cout 0 多重集組合數 n種物品,第...
多重集組合數問題
參考 題目 有n種物品,第i種物品有a個.不同種類的物品可以互相區分,但相同種類的無法區分.從這些物品中取出m個,有多少種取法?求出數模m的餘數.例如 有n 3種物品,每種a 個,取出m 3個,取法result 6 0 0 3,0 1 2,0 2 1,1 0 2,1 1 1,1 2 0 dp i j...