零钱兑换I
思路
題目中說每種硬幣的數(shù)量是無限的,可以看出是典型的完全背包問題。
確定dp數(shù)組以及下標(biāo)的含義
dp[ j ]:湊足總額為 j 所需錢幣的最少個(gè)數(shù)為 dp[ j ]
確定遞推公式
得到dp[j](考慮coins[i]),只有一個(gè)來源,dp[j - coins[i]](沒有考慮coins[i])。
湊足總額為j - coins[i]的最少個(gè)數(shù)為dp[j - coins[i]],那么只需要加上一個(gè)錢幣coins[i]即dp[j - coins[i]] + 1就是dp[j](考慮coins[i])
所以dp[j] 要取所有 dp[j - coins[i]] + 1 中最小的。
遞推公式:dp[j] = min(dp[j - coins[i]] + 1, dp[j]);
dp數(shù)組如何初始化
首先湊足總金額為0所需錢幣的個(gè)數(shù)一定是0,那么dp[0] = 0;
其他下標(biāo)對應(yīng)的數(shù)值呢?
考慮到遞推公式的特性,dp[j]必須初始化為一個(gè)最大的數(shù),否則就會在min(dp[j - coins[i]] + 1, dp[j])比較的過程中被初始值覆蓋。
所以下標(biāo)非0的元素都是應(yīng)該是最大值。
代碼如下:
vector<int> dp(amount + 1, INT_MAX); dp[0] = 0;確定遍歷順序
本題求錢幣最小個(gè)數(shù),那么錢幣有順序和沒有順序都可以,都不影響錢幣的最小個(gè)數(shù)。
所以本題并不強(qiáng)調(diào)集合是組合還是排列。
如果求組合數(shù)就是外層for循環(huán)遍歷物品,內(nèi)層for遍歷背包。
如果求排列數(shù)就是外層for遍歷背包,內(nèi)層for循環(huán)遍歷物品。
在動(dòng)態(tài)規(guī)劃專題中求組合數(shù)是:518.零錢兌換II,求排列數(shù)是:377. 組合總和 Ⅳ。
所以本題的兩個(gè)for循環(huán)的關(guān)系是:外層for循環(huán)遍歷物品,內(nèi)層for遍歷背包或者外層for遍歷背包,內(nèi)層for循環(huán)遍歷物品都是可以的!
那么我采用coins放在外循環(huán),target在內(nèi)循環(huán)的方式。
本題錢幣數(shù)量可以無限使用,那么是完全背包。所以遍歷的內(nèi)循環(huán)是正序
綜上所述,遍歷順序?yàn)?#xff1a;coins(物品)放在外循環(huán),target(背包)在內(nèi)循環(huán)。且內(nèi)循環(huán)正序。
舉例推導(dǎo)dp數(shù)組
以輸入:coins = [1, 2, 5], amount = 5為例
對于遍歷方式遍歷背包放在外循環(huán),遍歷物品放在內(nèi)循環(huán)也是可以的
class Solution { public:int coinChange(vector<int>& coins, int amount) {vector<int> dp(amount+1,INT_MAX);dp[0]=0;for(int jj=0;jj<=amount;jj++){for(int ii=0;ii<coins.size();ii++){if(jj>=coins[ii]&&dp[jj-coins[ii]]!=INT_MAX) dp[jj]=min(dp[jj],dp[jj-coins[ii]]+1);}}if(dp[amount]==INT_MAX) return -1;return dp[amount];} };總結(jié)