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