成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

0-1背包問題,你該了解這些!

開發 前端
有n件物品和一個最多能背重量為w 的背包。第i件物品的重量是weight[i],得到的價值是value[i] 。每件物品只能用一次,求解將哪些物品裝入背包里物品價值總和最大。

關于01背包問題,你該了解這些!

這周我們正式開始講解背包問題!

但說實話,背包九講對于小白來說確實不太友好,看起來還是有點費勁的,而且都是偽代碼理解起來也吃力。

對于面試的話,其實掌握01背包,和完全背包,就夠用了,最多可以再來一個多重背包。

如果這幾種背包,分不清,我這里畫了一個圖,如下:

分割等和子集1

至于背包九講中其他背包,面試幾乎不會問,都是競賽級別的了,leetcode上連多重背包的題目都沒有,所以題庫也告訴我們,01背包和完全背包就夠用了。

而完全背包又是也是01背包稍作變化而來,即:完全背包的物品數量是無限的。

所以背包問題的理論基礎重中之重是01背包,一定要理解透!

leetcode上沒有純01背包的問題,都是01背包應用方面的題目,也就是需要轉化為01背包問題。

所以我先通過純01背包問題,把01背包原理講清楚,后續再講解leetcode題目的時候,重點就是講解如何轉化為01背包問題了。

之前可能有些錄友已經可以熟練寫出背包了,但只要把這個文章仔細看完,相信你會意外收獲!

01 背包

有n件物品和一個最多能背重量為w 的背包。第i件物品的重量是weight[i],得到的價值是value[i] 。每件物品只能用一次,求解將哪些物品裝入背包里物品價值總和最大。

動態規劃-背包問題

這是標準的背包問題,以至于很多同學看了這個自然就會想到背包,甚至都不知道暴力的解法應該怎么解了。

這樣其實是沒有從底向上去思考,而是習慣性想到了背包,那么暴力的解法應該是怎么樣的呢?

每一件物品其實只有兩個狀態,取或者不取,所以可以使用回溯法搜索出所有的情況,那么時間復雜度就是,這里的n表示物品數量。

所以暴力的解法是指數級別的時間復雜度。進而才需要動態規劃的解法來進行優化!

在下面的講解中,我舉一個例子:

背包最大重量為4。

物品為:

  重量 價值
物品0 1 15
物品1 3 20
物品2 4 30

問背包能背的物品最大價值是多少?

以下講解和圖示中出現的數字都是以這個例子為例。

二維dp數組01背包

依然動規五部曲分析一波。

確定dp數組以及下標的含義

對于背包問題,有一種寫法, 是使用二維數組,即dp[i][j] 表示從下標為[0-i]的物品里任意取,放進容量為j的背包,價值總和最大是多少。

只看這個二維數組的定義,大家一定會有點懵,看下面這個圖:

動態規劃-背包問題1

要時刻記著這個dp數組的含義,下面的一些步驟都圍繞這dp數組的含義進行的,如果哪里看懵了,就來回顧一下i代表什么,j又代表什么。

確定遞推公式

再回顧一下dp[i][j]的含義:從下標為[0-i]的物品里任意取,放進容量為j的背包,價值總和最大是多少。

那么可以有兩個方向推出來dp[i][j],

  • 不放物品i:由dp[i - 1][j]推出,即背包容量為j,里面不放物品i的最大價值,此時dp[i][j]就是dp[i - 1][j]。(其實就是當物品i的重量大于背包j的重量時,物品i無法放進背包中,所以被背包內的價值依然和前面相同。)
  • 放物品i:由dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]] 為背包容量為j - weight[i]的時候不放物品i的最大價值,那么dp[i - 1][j - weight[i]] + value[i] (物品i的價值),就是背包放物品i得到的最大價值

所以遞歸公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

dp數組如何初始化

關于初始化,一定要和dp數組的定義吻合,否則到遞推公式的時候就會越來越亂。

首先從dp[i][j]的定義出發,如果背包容量j為0的話,即dp[i][0],無論是選取哪些物品,背包價值總和一定為0。如圖:

動態規劃-背包問題2

在看其他情況。

狀態轉移方程 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出i 是由 i-1 推導出來,那么i為0的時候就一定要初始化。

dp[0][j],即:i為0,存放編號0的物品的時候,各個容量的背包所能存放的最大價值。

那么很明顯當 j < weight[0]的時候,dp[0][j] 應該是 0,因為背包容量比編號0的物品重量還小。

當j >= weight[0]時,dp[0][j] 應該是value[0],因為背包容量放足夠放編號0物品。

代碼初始化如下:

  1. for (int j = 0 ; j < weight[0]; j++) {  // 當然這一步,如果把dp數組預先初始化為0了,這一步就可以省略,但很多同學應該沒有想清楚這一點。 
  2.     dp[0][j] = 0; 
  3. // 正序遍歷 
  4. for (int j = weight[0]; j <= bagweight; j++) { 
  5.     dp[0][j] = value[0]; 

此時dp數組初始化情況如圖所示:

動態規劃-背包問題7

dp[0][j] 和 dp[i][0] 都已經初始化了,那么其他下標應該初始化多少呢?

其實從遞歸公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出dp[i][j] 是由左上方數值推導出來了,那么 其他下標初始為什么數值都可以,因為都會被覆蓋。

初始-1,初始-2,初始100,都可以!

但只不過一開始就統一把dp數組統一初始為0,更方便一些。

如圖:

動態規劃-背包問題10

最后初始化代碼如下:

  1. // 初始化 dp 
  2. vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0)); 
  3. for (int j = weight[0]; j <= bagweight; j++) { 
  4.     dp[0][j] = value[0]; 

費了這么大的功夫,才把如何初始化講清楚,相信不少同學平時初始化dp數組是憑感覺來的,但有時候感覺是不靠譜的。

確定遍歷順序

在如下圖中,可以看出,有兩個遍歷的維度:物品與背包重量

動態規劃-背包問題3

那么問題來了,先遍歷 物品還是先遍歷背包重量呢?

其實都可以!!但是先遍歷物品更好理解。

那么我先給出先遍歷物品,然后遍歷背包重量的代碼。

  1. // weight數組的大小 就是物品個數 
  2. for(int i = 1; i < weight.size(); i++) { // 遍歷物品 
  3.     for(int j = 0; j <= bagweight; j++) { // 遍歷背包容量 
  4.         if (j < weight[i]) dp[i][j] = dp[i - 1][j]; 
  5.         else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 
  6.  
  7.     } 

先遍歷背包,再遍歷物品,也是可以的!(注意我這里使用的二維dp數組)

例如這樣:

  1. // weight數組的大小 就是物品個數 
  2. for(int j = 0; j <= bagweight; j++) { // 遍歷背包容量 
  3.     for(int i = 1; i < weight.size(); i++) { // 遍歷物品 
  4.         if (j < weight[i]) dp[i][j] = dp[i - 1][j]; 
  5.         else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 
  6.     } 

為什么也是可以的呢?

要理解遞歸的本質和遞推的方向。

dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 遞歸公式中可以看出dp[i][j]是靠dp[i-1][j]和dp[i - 1][j - weight[i]]推導出來的。

dp[i-1][j]和dp[i - 1][j - weight[i]] 都在dp[i][j]的左上角方向(包括正上方向),那么先遍歷物品,再遍歷背包的過程如圖所示:

動態規劃-背包問題5

再來看看先遍歷背包,再遍歷物品呢,如圖:

動態規劃-背包問題6

大家可以看出,雖然兩個for循環遍歷的次序不同,但是dp[i][j]所需要的數據就是左上角,根本不影響dp[i][j]公式的推導!

但先遍歷物品再遍歷背包這個順序更好理解。

其實背包問題里,兩個for循環的先后循序是非常有講究的,理解遍歷順序其實比理解推導公式難多了。

舉例推導dp數組

來看一下對應的dp數組的數值,如圖:

動態規劃-背包問題4

最終結果就是dp[2][4]。

建議大家此時自己在紙上推導一遍,看看dp數組里每一個數值是不是這樣的。

做動態規劃的題目,最好的過程就是自己在紙上舉一個例子把對應的dp數組的數值推導一下,然后在動手寫代碼!

很多同學做dp題目,遇到各種問題,然后憑感覺東改改西改改,怎么改都不對,或者稀里糊涂就改過了。

主要就是自己沒有動手推導一下dp數組的演變過程,如果推導明白了,代碼寫出來就算有問題,只要把dp數組打印出來,對比一下和自己推導的有什么差異,很快就可以發現問題了。

完整c++測試代碼

  1. void test_2_wei_bag_problem1() { 
  2.     vector<int> weight = {1, 3, 4}; 
  3.     vector<int> value = {15, 20, 30}; 
  4.     int bagweight = 4; 
  5.  
  6.     // 二維數組 
  7.     vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0)); 
  8.  
  9.     // 初始化 
  10.     for (int j = weight[0]; j <= bagweight; j++) { 
  11.         dp[0][j] = value[0]; 
  12.     } 
  13.  
  14.     // weight數組的大小 就是物品個數 
  15.     for(int i = 1; i < weight.size(); i++) { // 遍歷物品 
  16.         for(int j = 0; j <= bagweight; j++) { // 遍歷背包容量 
  17.             if (j < weight[i]) dp[i][j] = dp[i - 1][j]; 
  18.             else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 
  19.  
  20.         } 
  21.     } 
  22.  
  23.     cout << dp[weight.size() - 1][bagweight] << endl; 
  24.  
  25. int main() { 
  26.     test_2_wei_bag_problem1(); 

總結

講了這么多才剛剛把二維dp的01背包講完,這里大家其實可以發現最簡單的是推導公式了,推導公式估計看一遍就記下來了,但難就難在如何初始化和遍歷順序上。

可能有的同學并沒有注意到初始化 和 遍歷順序的重要性,我們后面做力扣上背包面試題目的時候,大家就會感受出來了。

本文轉載自微信公眾號「代碼隨想錄」,可以通過以下二維碼關注。轉載本文請聯系代碼隨想錄公眾號。

 

責任編輯:武曉燕 來源: 代碼隨想錄
相關推薦

2021-01-19 05:46:45

背包數組容量

2021-04-13 07:58:38

背包代碼模式

2021-02-09 09:55:24

動態規劃

2021-01-04 08:37:53

動態規劃DP

2018-10-15 12:42:21

2021-07-13 14:03:24

二叉樹滿二叉樹完全二叉樹

2021-04-27 07:52:18

跳槽數據分析

2021-05-18 08:02:40

面試面試問題職業規劃

2021-03-29 09:37:17

SpringBoot常用注解Spring Boot

2023-06-26 19:25:18

效率消息中心業務線

2020-10-29 10:26:28

DevOps軟件自動化

2021-05-11 07:39:58

跳槽談薪工作

2020-04-03 18:43:21

大數據Hadoop數據

2021-01-07 05:40:13

BLE模塊Android

2017-06-14 15:07:58

機房管理服務器

2017-01-09 16:40:07

React NatiAndroid 開發

2010-08-27 15:24:39

機房監控

2023-12-07 08:13:58

Java開發

2022-12-23 08:03:45

西瓜業務SEO前端

2022-10-26 07:21:15

網絡視頻開發
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产日韩一区二区 | 国产91久久久久 | 一区二区三区视频 | 精品久久久久久 | 毛片视频观看 | 国产高清在线精品 | 久久久蜜臀国产一区二区 | 新91视频网 | 亚洲免费视频一区 | 91超碰在线 | 欧美日韩亚 | 草草在线观看 | 国产午夜精品一区二区三区嫩草 | 少妇一级淫片免费放播放 | 在线视频成人 | 日韩电影一区 | 国产精品久久久久久吹潮 | 日本国产一区二区 | 中文成人在线 | 午夜一级黄色片 | 久久久久久久久久久久久9999 | 99tv| 亚洲精品一区二区三区在线 | 国产一区二区三区久久久久久久久 | 久久精品二区亚洲w码 | 中文一区| 免费成人在线网 | 欧美日韩成人影院 | 日韩视频一区在线观看 | 中文字幕免费视频 | 精品一二区 | 久久综合九九 | 日本超碰 | 亚洲国产情侣自拍 | 中文字幕在线观看 | 国产日韩精品久久 | 一级片在线视频 | 中文字幕一区二区三区精彩视频 | 国产精品国产成人国产三级 | 手机看黄av免费网址 | www.欧美|