1. 程式人生 > >算法學習——動態規劃講解

算法學習——動態規劃講解

根據 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動態規劃
算法數據結構面試通關(經驗全集)

算法學習——動態規劃講解