動態規劃演算法學習筆記
阿新 • • 發佈:2020-12-20
技術標籤:演算法和資料結構
常用演算法之動態規劃學習筆記
動態規劃問題的一般形式是求最值,求解動態規劃的核心問題是窮舉,窮舉所有的答案找最值。
因為這類問題存在 重疊子問題 的情況,所以需要 備忘錄(自頂向下)或者DP(Dynamic Programming) table(自底向上) 來優化窮舉過程。一定存在最優子結構(子問題的解答互相之間不影響),才能通過子問題的最值得到原問題最值。
因此有動態規劃三要素:重疊子問題、最優子結構、狀態轉移方程。
如斐波那契數列可以用動態規劃的思想進行優化,下面以經典樣例 湊零錢問題 為例,闡釋動態規劃問題的解決方式。
問題描述:
給你k種⾯值的硬幣,⾯值分別為c1, c2 … ck ,每種硬幣的數量⽆限,再給⼀個總⾦額amount ,問你最少需要⼏枚硬幣湊出這個⾦額,如果不可能湊出,演算法返回 -1 。
int coinChange(int [] coins, int amount)
狀態轉移方程:
重點是列出狀態轉移方程,主要思路如下:
- 先確定狀態,原問題和子問題中變化的變數,硬幣數量無限,所以唯一的狀態是目標金額amount
- 確定dp 函式的定義:當前的目標金額為n,至少需要 dp(n) 個硬幣湊整
- 確定選擇並擇優,列出n取不同情況時的dp(n)取值,同時明確base case,也就是n = 0或n < 0特殊情況時的返回值
得到狀態轉移方程:
對應的Java程式碼:
public int coinChange(int[] coins, int amount) {
int [] dp = new int[amount + 1];
dp[0] = 0;
for (int i = 1;i <= amount;i++) {
boolean hasChange = false;
int min = amount;
for (int coin : coins) {
int index = i - coin;
if (index >= 0 && dp[index] >= 0) {
int value = dp[index] + 1;
if (min > value) {
min = value;
}
hasChange = true;
}
}
if (!hasChange) {
dp[i] = -1;
} else {
dp[i] = min;
}
}
return dp[amount];
}