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

1、概念

經過把原問題分解爲相對簡單的子問題的方式求解複雜問題的方法。動態規劃經常適用於有重疊子問題和最優子結構性質的問題。html

2、題型特色

  • 計數
    • 有多少種方式走到最右下角
  • 求最大值最小值
    • 從左上角走到右下角的最大數字和
  • 求存在性
    • 可否選出k個數使得和爲sum

3、如何使用動態規劃

這裏先看一道LeetCode題。從這道題來學習如何使用動態規劃。java

題目連接LeetCode-322面試

Coin Change算法

給定不一樣面額的硬幣 coins 和一個總金額 amount。
編寫一個函數來計算能夠湊成總金額所需的最少的硬幣個數。
若是沒有任何一種硬幣組合能組成總金額,返回 -1。

示例數據結構

輸入: coins = [1, 2, 5], amount = 11
輸出: 3 
解釋: 11 = 5 + 5 + 1

該題是一個求最大最小的動態規劃算法題。ide

與遞歸解法相比,沒有重複計算。函數

3.1 組成部分一:肯定狀態

肯定狀態須要有兩個注意的點:最後一步子問題學習

1.最後一步spa

確定是$k$枚硬幣加起來等於11。最後一枚硬幣值假設是$a_k$,則剩下的$k-1$枚硬幣的值爲$11-a_k$。code

因爲是最優解,則11-$a_k$的硬幣數必定是最少。

2.子問題

將原問題轉換爲子問題,最少用多少枚硬幣拼出$11-a_k$

那麼$a_k$究竟是多少,由於有3枚硬幣,因此只多是一、二、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}$

意思就是獲取面值大小爲X最少須要的硬幣數 = 從(最後一個硬幣選1時,剩下的要湊面值爲X-1所須要的最少硬幣數,由於最後一個硬幣選了1,因此硬幣數要+1,即f[x-1]+1)、(f[X-2]+1)、(f[X-5]+1)中選擇一個最少的硬幣數。

3.3 組成部分三:初始條件和邊界狀況

設置初始值,考慮邊界狀況。

3.4 組成部分四:計算順序**

從上到下,從左到右。

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++){
                // 考慮輸入[2],4,則須要保證f[x-coins[i]] != Integer.MAX_VALUE,即f[x-coins[i]]必需要是存在的狀態
                if(x >=coins[i] && f[x-coins[i]] != Integer.MAX_VALUE){
                    f[x] = Math.min(f[x-coins[i]]+1,f[x]);
                }
            }
        }
        // 考慮輸入[2],3,則amount = -1
        if(f[amount] == Integer.MAX_VALUE){
            return -1;
        }
        return f[amount];
    }
}

參考文檔

動態規劃

參考視頻

動態規劃入門 Introduction to Dynamic Programming ACM專題講解:DP動態規劃 算法數據結構面試通關(經驗全集)

相關文章
相關標籤/搜索