零钱兑换II
但本題和純完全背包不一樣,純完全背包是能否湊成總金額,而本題是要求湊成總金額的個(gè)數(shù)!
注意題目描述中是湊成總金額的硬幣組合數(shù),為什么強(qiáng)調(diào)是組合數(shù)呢?
例如示例一:
5 = 2 + 2 + 1
5 = 2 + 1 + 2
這是一種組合,都是 2 2 1。
如果問(wèn)的是排列數(shù),那么上面就是兩種排列了。
組合不強(qiáng)調(diào)元素之間的順序,排列強(qiáng)調(diào)元素之間的順序
確定dp數(shù)組以及下標(biāo)的含義
dp[j]:湊成總金額j的貨幣組合數(shù)為dp[j]
確定遞推公式
dp[j] (考慮coins[i]的組合總和) 就是所有的dp[j - coins[i]](不考慮coins[i])相加。
所以遞推公式:dp[j] += dp[j - coins[i]];
dp數(shù)組如何初始化
首先dp[0]一定要為1,dp[0] = 1是 遞歸公式的基礎(chǔ)。
從dp[i]的含義上來(lái)講就是,湊成總金額0的貨幣組合數(shù)為1。
下標(biāo)非0的dp[j]初始化為0,這樣累計(jì)加dp[j - coins[i]]的時(shí)候才不會(huì)影響真正的dp[j]
確定遍歷順序
純完全背包是能湊成總和就行,不用管怎么湊的。
本題是求湊成總和的方案?jìng)€(gè)數(shù),且每個(gè)方案?jìng)€(gè)數(shù)是為組合數(shù)。
那么本題,兩個(gè)for循環(huán)的先后順序可就有說(shuō)法了。
我們先來(lái)看 外層for循環(huán)遍歷物品(錢幣),內(nèi)層for遍歷背包(金錢總額)的情況。
代碼如下:
for (int i = 0; i < coins.size(); i++) { // 遍歷物品// 遍歷背包容量,j一開(kāi)始為coins[i]的原因是保證背包可以放得下所遍歷的物品,避免背包容量<0//因?yàn)槲锲返闹亓恳欢?gt;=0for (int j = coins[i]; j <= amount; j++) { dp[j] += dp[j - coins[i]];} }假設(shè):coins[0] = 1,coins[1] = 5。
那么就是先把1加入計(jì)算,然后再把5加入計(jì)算,得到的方法數(shù)量只有{1, 5}這種情況。而不會(huì)出現(xiàn){5, 1}的情況。
所以這種遍歷順序中dp[j]里計(jì)算的是組合數(shù)!
如果把兩個(gè)for交換順序,代碼如下:
for (int j = 0; j <= amount; j++) { // 遍歷背包容量for (int i = 0; i < coins.size(); i++) { // 遍歷物品if (j - coins[i] >= 0) dp[j] += dp[j - coins[i]];} }背包容量的每一個(gè)值,都是經(jīng)過(guò) 1 和 5 的計(jì)算,包含了{(lán)1, 5} 和 {5, 1}兩種情況。
此時(shí)dp[j]里算出來(lái)的就是排列數(shù)!
舉例推導(dǎo)dp數(shù)組
輸入: amount = 5, coins = [1, 2, 5] ,dp狀態(tài)圖如下:
在求裝滿背包有幾種方案的時(shí)候,認(rèn)清遍歷順序是非常關(guān)鍵的。
如果求組合數(shù)就是外層for循環(huán)遍歷物品,內(nèi)層for遍歷背包。
如果求排列數(shù)就是外層for遍歷背包,內(nèi)層for循環(huán)遍歷物品。
總結(jié)