日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

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

發(fā)布時(shí)間:2023/12/16 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一个简单的例子由易到难理解动态规划 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在本文中,我們先用最簡(jiǎn)單的方法來解決問題,然后一步步進(jìn)階,在這個(gè)不斷優(yōu)化算法的過程中理解動(dòng)態(tài)規(guī)劃。

力扣 322. 零錢兌換 問題

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

    1. 暴力遞歸

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

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

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

    public int coinChange(int[] coins, int amount) {//確定遞歸結(jié)束的條件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; }

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


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

    但這個(gè)方法存在重疊子問題,即存在大量的重復(fù)計(jì)算。有很多問題我們已經(jīng)計(jì)算過,但當(dāng)遞歸到那里時(shí),還是要再算一次。

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

    2. 帶備忘錄的遞歸解法

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

    每次計(jì)算完后,將值存入一個(gè)數(shù)組中,每次計(jì)算前判斷一下備忘錄中有沒有存,有則可以直接獲取,不需要再進(jìn)行漫長(zhǎng)的遞歸運(yùn)算了。

    int[] count; public int coinChange(int[] coins, int amount) {//建立數(shù)組,進(jìn)行初始化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]; }

    現(xiàn)在似乎已經(jīng)很完善了,但備忘錄解法是 自頂向下 的,是從一個(gè)規(guī)模較大的原問題,一層一層向下分解成子問題,直至底層,然后再逐層返回結(jié)果。

    動(dòng)態(tài)規(guī)劃是 自底向上 的解出最優(yōu)值。下面來改造一下這個(gè)方法。

    3. dp 數(shù)組的迭代解法

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

    public int coinChange(int[] coins, int amount) {int[] dp = new int[amount + 1];Arrays.fill(dp, amount + 1);dp[0] = 0;// 外層 for 循環(huán)在遍歷所有狀態(tài)的所有取值// 然后存入 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]; }

    這就是最后的動(dòng)態(tài)規(guī)劃的解法了。

    動(dòng)態(tài)規(guī)劃最核心的是寫出狀態(tài)轉(zhuǎn)移方程,這也是最困難的一步,多多練習(xí),熟能生巧。

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

    總結(jié)

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

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。