日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

一个简单的例子由易到难理解动态规划

發布時間:2023/12/16 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一个简单的例子由易到难理解动态规划 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在本文中,我們先用最簡單的方法來解決問題,然后一步步進階,在這個不斷優化算法的過程中理解動態規劃。

力扣 322. 零錢兌換 問題

  • 給你一個整數數組 coins ,表示不同面額的硬幣;以及一個整數 amount ,表示總金額。
  • 計算并返回可以湊成總金額所需的 最少的硬幣個數 。如果沒有任何一種硬幣組合能組成總金額,返回 -1 。【你可以認為每種硬幣的數量是無限的。】
  • public int coinChange(int[] coins, int amount) {}

    1. 暴力遞歸

    假設 coins[] = [2, 4, 5],amount = 10,想要求 amount = 10 可以求
    min ( 1 + coinChange(coins, amount - 2), 1 + coinChange(coins, amount - 4), 1 + coinChange(coins, amount - 5) )

    即假設已經知道了 amount = 8、amount = 6、amount = 5 時 所需的最少的硬幣個數, 那只要再加 1 個硬幣就可以湊夠 10,在這三者中選最少的一個就是答案。

    繼續想的話,求 amount = 8,也是同樣的方法,就是不斷往下遞歸。

    public int coinChange(int[] coins, int amount) {//確定遞歸結束的條件if(amount == 0) return 0;if(amount < 0) return -1;int res = Integer.MAX_VALUE;//遍歷求各 coinChange(amount-coin) 的值,求出最小值for(int coin: coins){int subProblem = coinChange(coins, amount-coin);if(subProblem == -1) continue;res = Math.min(res, subProblem + 1);}return res==Integer.MAX_VALUE ? -1 : res; }

    為了更好理解,來畫一下遞歸樹


    從上圖就可以看出來,這種方法就是在用遞歸把所有的可能窮舉出來,然后取最小值。

    但這個方法存在重疊子問題,即存在大量的重復計算。有很多問題我們已經計算過,但當遞歸到那里時,還是要再算一次。

    所以為解決這個問題,下面采用帶備忘錄的遞歸解法。

    2. 帶備忘錄的遞歸解法

    帶備忘錄就是把已經計算過的值存起來,以此來減少不必要的計算。

    每次計算完后,將值存入一個數組中,每次計算前判斷一下備忘錄中有沒有存,有則可以直接獲取,不需要再進行漫長的遞歸運算了。

    int[] count; public int coinChange(int[] coins, int amount) {//建立數組,進行初始化count = new int[amount+1];Arrays.fill(count, -2);return(coin(coins, amount)); } public int coin(int[] coins, int amount){if(amount == 0) return 0;if(amount < 0) return -1;//判斷是否存入備忘錄if(count[amount] != -2)return count[amount];int res = Integer.MAX_VALUE;for(int coin:coins){int subProblem = coin(coins, amount-coin);if(subProblem == -1) continue;res = Math.min(res, subProblem+1);}//存入count[amount] = (res == Integer.MAX_VALUE) ? -1 : res;return count[amount]; }

    現在似乎已經很完善了,但備忘錄解法是 自頂向下 的,是從一個規模較大的原問題,一層一層向下分解成子問題,直至底層,然后再逐層返回結果。

    動態規劃是 自底向上 的解出最優值。下面來改造一下這個方法。

    3. dp 數組的迭代解法

    采用 自底向上 就是要從底層狀態一直算到所求狀態
    定義一個 dp 數組,dp[i] 表示為 amount = i 時所需的最少的硬幣個數

    public int coinChange(int[] coins, int amount) {int[] dp = new int[amount + 1];Arrays.fill(dp, amount + 1);dp[0] = 0;// 外層 for 循環在遍歷所有狀態的所有取值// 然后存入 dp[i] 中for(int i=1; i<dp.length; i++){for(int coin: coins){if(i-coin < 0) continue;dp[i] =Math.min(dp[i], 1 + dp[i-coin]);}}return dp[amount] == amount + 1 ? -1 : dp[amount]; }

    這就是最后的動態規劃的解法了。

    動態規劃最核心的是寫出狀態轉移方程,這也是最困難的一步,多多練習,熟能生巧。

    本文源于學習 labuladong的算法小抄 ,大佬寫的明白易懂,沒有什么晦澀的地方。

    總結

    以上是生活随笔為你收集整理的一个简单的例子由易到难理解动态规划的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。