算法學習——動態規劃講解
阿新 • • 發佈:2019-05-13
根據 ref process 技術 ces 入門 最大值最小值 圖片 使用遞歸
一、概念
通過把原問題分解為相對簡單的子問題的方式求解復雜問題的方法。動態規劃常常適用於有重疊子問題和最優子結構性質的問題。
二、題型特點
- 計數
- 有多少種方式走到最右下角
- 求最大值最小值
- 從左上角走到右下角的最大數字和
- 求存在性
- 能否選出k個數使得和為sum
三、如何使用動態規劃
這裏先看一道LeetCode題。從這道題來學習如何使用動態規劃。
題目鏈接LeetCode-322
Coin Change
給定不同面額的硬幣 coins 和一個總金額 amount。
編寫一個函數來計算可以湊成總金額所需的最少的硬幣個數。
如果沒有任何一種硬幣組合能組成總金額,返回 -1。
示例
輸入: coins = [1, 2, 5], amount = 11 輸出: 3 解釋: 11 = 5 + 5 + 1
該題是一個求最大最小的動態規劃算法題。
與遞歸解法相比,沒有重復計算。
3.1 組成部分一:確定狀態
確定狀態需要有兩個註意的點:最後一步
、子問題
1.最後一步
肯定是\(k\)枚硬幣加起來等於11。最後一枚硬幣值假設是\(a_k\),則剩下的\(k-1\)枚硬幣的值為\(11-a_k\)。
由於是最優解,則11-\(a_k\)的硬幣數一定是最少。
2.子問題
將原問題轉換為子問題,最少用多少枚硬幣拼出\(11-a_k\)
那麽\(a_k\)到底是多少,因為有3枚硬幣,所以只可能是1、2、5中的一個。
子問題方程如下:
\(f(11) = min\{f(11-1)+1,f(11-2)+1,f(11-5)+1\}\)
f(11)為拼出面值為11所需的最少硬幣數。
根據以上,使用遞歸的解法:
public class Dp1 { public int getMinCoin(int X) { if (X == 0) return 0; int res = 10000; if (X >= 1) { res = Math.min(getMinCoin(X - 1)+1, res); } if (X >= 2) { res = Math.min(getMinCoin(X - 2)+1, res); } if (X >= 5) { res = Math.min(getMinCoin(X - 5)+1, res); } return res; } public static void main(String[] args) { Dp1 dp1 = new Dp1(); int result = dp1.getMinCoin(11); System.out.println(result); } }
使用遞歸來解決,有比較多的重復計算,效率比較低。
動態規劃會保存計算結果,來避免遞歸重復計算的問題。
3.2 組成部分二:轉移方程
動態規劃的解法
狀態f[X]表示,面值為X所需的最小硬幣數。
對於任意的X,滿足
\(f[X] = min\{f[X-1]+1,f[X-2]+1,f[X-5]+1\}\)
3.3 組成部分三:初始條件和邊界情況
設置初始值,考慮邊界情況。
3.4 組成部分四:計算順序**
從上到下,從左到右。
四、LeetCode題完整解法
class Solution {
public int coinChange(int[] coins, int amount) {
int[] f = new int[amount+1];
int coin_num = coins.length;
//初始條件
f[0] = 0;
//f[x] = min{f[x-c1]+1,f[x-c2]+1,f[x-c3]+1}
for(int x = 1;x<=amount;x++){
f[x] = Integer.MAX_VALUE;
for(int i = 0;i<coin_num;i++){
if(x >=coins[i] && f[x-coins[i]] != Integer.MAX_VALUE){
f[x] = Math.min(f[x-coins[i]]+1,f[x]);
}
}
}
if(f[amount] == Integer.MAX_VALUE){
return -1;
}
return f[amount];
}
}
參考文檔
動態規劃
參考視頻
動態規劃入門 Introduction to Dynamic Programming
ACM專題講解:DP動態規劃
算法數據結構面試通關(經驗全集)
算法學習——動態規劃講解