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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java入门算法(动态规划篇2:01背包精讲)

發布時間:2023/12/19 java 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java入门算法(动态规划篇2:01背包精讲) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本專欄已參加蓄力計劃,感謝讀者支持?

往期文章

一. Java入門算法(貪心篇)丨蓄力計劃
二. Java入門算法(暴力篇)丨蓄力計劃
三. Java入門算法(排序篇)丨蓄力計劃
四. Java入門算法(遞歸篇)丨蓄力計劃
五. Java入門算法(雙指針篇)丨蓄力計劃
六. Java入門算法(數據結構篇)丨蓄力計劃
七. Java入門算法(滑動窗口篇)丨蓄力計劃
八. Java入門算法(動態規劃篇1:初識動規)
九. Java入門算法(動態規劃篇2:01背包精講)


動態規劃篇2

  • 往期文章
  • 01背包
    • 問題描述
    • 分析
    • 進階


01背包

網上有非常多的文章對01背包進行講解,變量名繁雜,對初學者不怎么友好。在這篇文章里,我盡量講得簡單,不作一些多余的贅述。

不會有人不知道背包是什么吧?


問題描述

???????把n種物品裝進一個背包,物品 i 的重量是w[ i ],價值是c[ i ],背包的容量是M,求能裝進背包的最大總價值。

注:w是存儲n個物品的重量的數組,c是存儲n個物品的價值的數組


分析

為啥叫01背包呢?因為對于每件物品,只有 不拿(0) 與 拿(1) 兩種狀態。

動態規劃解01背包,關鍵是填二維表dp:

  • 行 i 指的是我們當前要考慮裝 i 個物品
  • 列 j 表示的是背包剩余容量
  • dp [i] [j] 的值是指 i 個物品裝進容量為 j 的背包的最大總價值

當 i 等于物品總量n、j 等于背包容量M的時候,dp [i] [j] 的值就是問題的解。下面根據例子輸入,邊填表邊分析。

輸入:n = 4, M = 10 w[] = [2, 3, 4, 7] c[] = [1, 3, 5, 9] (4個物品裝進容量為10的背包)
  • 初始化第0行為全0,沒有物品,不管容量多大,價值只能是0
  • 初始化第0列為全0,容量為0,裝不下任何物品,價值只能是0


現在只考慮裝 1 件物品(i = 1)

  • dp[1][1] = 0,容量為1的背包裝不下第 1 件物品,因為它的重量為2


  • dp[1][2] = 1,此時容量為2的背包可以裝下物品1,而它的價值為1


    • 第1行后面容量>1的背包,都可以裝下物品1,因此它們的價值都為1


    現在考慮的是裝 2 件物品(i = 2)

    • dp[2][1] = 0,容量為1的背包即裝不下物品1(重量2)也裝不下物品2(重量3)


    • 容量為2的背包裝不下物品2,但可以裝下物品1
    • 因此dp[2][2] = dp[1][2] = 1


    此時引出第一種情況:

  • 當背包容量小于物品 i 的重量 w[i] 時,拿不了物品 i ,所以考慮拿 i - 1個物品的最大總價值。
  • 得到狀態轉移方程: dp[i][j] = dp[ i - 1][j] ( j < w[i])
  • if (j < w[i]) {dp[i][j] = dp[i - 1][j];}
    • 容量為3的背包既可以裝下物品2、也可以裝下物品1。要使價值最大,我們肯定會選裝物品2,這樣就是dp[2][3] = 3。
    • 此時的背包容量都用來裝物品2,已經滿了,裝不下物品1(對應dp[1][0] = 0)。
    • 但這只是我們主觀得出的選擇,計算機怎么知道是選裝物品1還是物品2呢?

    此時引出第二種情況:

  • 當背包容量大于物品 i 的重量 w[i] 時,可以拿物品 i ,此時需要考慮拿了物品 i 的價值大,還是不拿物品 i 的價值大
  • 不拿物品 i ,就是第一種情況,考慮拿 i - 1個物品的最大總價值
  • 拿物品 i ,就需要把物品 i 裝入到背包(+c[i])。但!還需要考慮裝完物品 i 的背包還可以裝多少(+ dp[ i - 1][ j - w[i]],即上述的 dp[1][0])
  • 得到狀態轉移方程: dp[i][j] = c[i] + dp[ i - 1][ j - w[i]] ( j >= w[i])

  • 如下,容量為5的背包,兩個物品都可以裝下

    所以dp[2][5] = c[2] + dp[1][5 - 3] 即 4 = 3 + 1

    if (j >= w[i]) {dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i]] + c[i]);}

    綜上,可以得到總的狀態轉移方程:

    dp[i][j]=dp[i?1][j],(j<w[i])dp[i][j] = dp[ i - 1][j] ,( j < w[i]) dp[i][j]=dp[i?1][j](j<w[i])
    dp[i][j]=c[i]+dp[i?1][j?w[i]],(j>=w[i])dp[i][j] = c[i] + dp[ i - 1][ j - w[i]], ( j >= w[i]) dp[i][j]=c[i]+dp[i?1][j?w[i]](j>=w[i])
    狀態轉移方程代表了我們接下來的選擇,根據它計算出表內的所有值,如下圖

    時間復雜度為O(n2),空間復雜度為O(M*n)


    得到題目答案:12

    完整代碼

    // 二維dppublic static int dp_2d(int n, int[] w, int[] c, int M) {int[][] dp = new int[n + 1][M + 1];for (int i = 1; i < n + 1; ++i) {for (int j = 1; j < M + 1; ++j) {if (j < w[i]) {dp[i][j] = dp[i - 1][j];}else{dp[i][j] = Math.max(dp[i - 1][j], c[i] + dp[i - 1][j - w[i]]);}}}return dp[n][M];}

    進階

    滾動數組

    • 由上述的填表順序可以發現,每次 dp[i][j] 的值都是由 i - 1 行左上方或正上方的dp值得出。因此可以嘗試把 [i] 這一維度去除,二維dp降成一維dp,通過不斷刷新一維dp表的值,模擬上述二維dp的填表過程。時間復雜度仍為O(n^2^),空間復雜度降為O(M)。
    • 但會發現若按照從左往右的順序填表,會把需要用到的值覆蓋,固填表方向需要反過來。


    完整代碼

    // 二維降一維,滾動數組public static int dp_1d(int n, int[] w, int[] c, int M) {int[] dp = new int[M + 1];for (int i = 1; i < n + 1; ++i) {for (int j = 1; j < M + 1; ++j) {if (j >= w[i]) {dp[j] = Math.max(dp[j], dp[j - w[i]] + c[i]);}}}return dp[M];}

    END

    參考資料:
    https://www.bilibili.com/video/BV1C7411K79wfrom=search&seid=9137630755457139754

    總結

    以上是生活随笔為你收集整理的Java入门算法(动态规划篇2:01背包精讲)的全部內容,希望文章能夠幫你解決所遇到的問題。

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