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
此時引出第一種情況:
- 容量為3的背包既可以裝下物品2、也可以裝下物品1。要使價值最大,我們肯定會選裝物品2,這樣就是dp[2][3] = 3。
- 此時的背包容量都用來裝物品2,已經滿了,裝不下物品1(對應dp[1][0] = 0)。
- 但這只是我們主觀得出的選擇,計算機怎么知道是選裝物品1還是物品2呢?
此時引出第二種情況:
如下,容量為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)。
- 但會發現若按照從左往右的順序填表,會把需要用到的值覆蓋,固填表方向需要反過來。
完整代碼
END
參考資料:
https://www.bilibili.com/video/BV1C7411K79wfrom=search&seid=9137630755457139754
總結
以上是生活随笔為你收集整理的Java入门算法(动态规划篇2:01背包精讲)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java入门算法(动态规划篇1:初识动规
- 下一篇: java美元兑换,(Java实现) 美元