Leetcoder Day37| 動態規劃part04 背包問題

01背包理論基礎

面試掌握01背包,完全背包和重背包就夠用了。

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

01 背包

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

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

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

舉例:背包最大重量為4。

物品為:

重量價值
物品0115
物品1320
物品2430

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

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

二維數組01背包

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

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

對于背包問題,有一種寫法, 是使用二維數組,即dp[i][j] 表示從下標為[0-i]的物品里任意取,放進容量為j的背包,價值總和最大是多少要時刻記著這個dp數組的含義,下面的一些步驟都圍繞這dp數組的含義進行的。

2. ?確定遞推公式

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

  • 不放物品i:由dp[i - 1][j]推出,即背包容量為j,里面不放物品i的最大價值,此時dp[i][j]就是dp[i - 1][j]。(其實就是當物品i的重量大于背包j的重量或背包剩余重量小于i的重量時,物品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])

3. dp數組如何初始化

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

首先從dp[i][j]的定義出發,如果背包容量j為0的話,即dp[i][0],無論是選取哪些物品,背包價值總和一定為0。i是由i-1推出來的,所以i為0的時候就一定要初始化。剛才討論過j=0的情況,那么i=0時,dp[0][j],即:存放編號0的物品時,各個容量的背包所能存放的最大價值。因此?j < weight[0]時,dp[0][j] 應該是 0,因為背包容量比編號0的物品重量還小。若j>=weight[0],dp[0][j]的值為value[0]。

dp[0][j] 和 dp[i][0] 初始化以后,其他位置都會從i-1或者j-weight[i]而來,因此都會被不斷地覆蓋,所以初始化為0即可。

4. 確定遍歷順序

在如下圖中,可以看出,有兩個遍歷的維度:物品與背包重量,從哪個方向遍歷都可以,因此我們就從物品開始遍歷。

public static void backValue(int[]value, int[] weight, int bagWeight){int num=value.length;int[][]dp=new int[num][bagWeight+1];for(int j=weight[0];j<bagWeight;j++){dp[0][j]=value[0];}for(int i=1;i<num;i++){//從物品開始遍歷for(int j=1;j<=bagWeight;j++){if(j<weight[i]) dp[i][j]=dp[i-1][j];else{dp[i][j] = Math.max(dp[i-1][j] , dp[i-1][j-weight[i]] + value[i]);}}}System.out.println(dp[num-1][bagWeight]);}   

一維數組01背包

上面的思路是用二維數組來解決01背包問題,還可以用滾動數組來解決,即把二維dp降維。

在使用二維數組的時候,遞推公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

其實可以發現如果把dp[i - 1]那一層拷貝到dp[i]上,表達式完全可以是:dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);

與其把dp[i - 1]這一層拷貝到dp[i]上,不如只用一個一維數組了,只用dp[j](一維數組,也可以理解是一個滾動數組)。

因此,動規五部曲分析如下:

1. 確定dp數組的定義

在一維dp數組中,dp[j]表示:容量為j的背包,所背的物品價值可以最大為dp[j]。

2. 一維dp數組的遞推公式

dp[j]為 容量為j的背包所背的最大價值,那么如何推導dp[j]呢?

dp[j]可以通過dp[j - weight[i]]推導出來,dp[j - weight[i]]表示容量為j - weight[i]的背包所背的最大價值。

dp[j - weight[i]] + value[i] 表示 容量為 j - 物品i重量 的背包 加上 物品i的價值。(也就是容量為j的背包,放入物品i了之后的價值即:dp[j])

此時dp[j]有兩個選擇,一個是取自己dp[j] 相當于 二維dp數組中的dp[i-1][j],即不放物品i,一個是取dp[j - weight[i]] + value[i],即放物品i,指定是取最大的,畢竟是求最大價值。

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

3. 一維dp數組如何初始化

dp[j]表示:容量為j的背包,所背的物品價值可以最大為dp[j],那么dp[0]就應該是0,因為背包容量為0所背的物品的最大價值就是0。

那么dp數組除了下標0的位置,初始為0,其他下標應該初始化多少呢?

看一下遞歸公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

dp數組在推導的時候一定是取價值最大的數,如果題目給的價值都是正整數那么非0下標都初始化為0就可以了。

這樣才能讓dp數組在遞歸公式的過程中取的最大的價值,而不是被初始值覆蓋了

那么我假設物品價值都是大于0的,所以dp數組初始化的時候,都初始為0就可以了。

4.?一維dp數組遍歷順序

二維dp遍歷的時候,背包容量是從小到大,而一維dp遍歷的時候,背包是從大到小。

因為倒序遍歷是為了保證物品i只被放入一次!。但如果一旦正序遍歷了,那么物品0就會被重復加入多次!

舉一個例子:物品0的重量weight[0] = 1,價值value[0] = 15

如果正序遍歷

dp[1] = dp[1 - weight[0]] + value[0] = 15

dp[2] = dp[2 - weight[0]] + value[0] = 30

此時dp[2]就已經是30了,意味著物品0,被放入了兩次,所以不能正序遍歷。

為什么倒序遍歷,就可以保證物品只放入一次呢?

倒序就是先算dp[2]

dp[2] = dp[2 - weight[0]] + value[0] = 15 (dp數組已經都初始化為0)

dp[1] = dp[1 - weight[0]] + value[0] = 15

所以從后往前循環,每次取得狀態不會和之前取得狀態重合,這樣每種物品就只取一次了。

為什么二維dp數組遍歷的時候不用倒序呢?

因為對于二維dp,dp[i][j]都是通過上一層即dp[i - 1][j]計算而來,本層的dp[i][j]并不會被覆蓋!

先遍歷物品嵌套遍歷背包容量,那可不可以先遍歷背包容量嵌套遍歷物品呢?不可以!

因為一維dp的寫法,背包容量一定是要倒序遍歷(原因上面已經講了),如果遍歷背包容量放在上一層,那么每個dp[j]就只會放入一個物品,即:背包里只放入了一個物品。

倒序遍歷的原因是,本質上還是一個對二維數組的遍歷,并且右下角的值依賴上一層左上角的值,因此需要保證左邊的值仍然是上一層的,從右向左覆蓋。

??一維和二維的區別:(1)一維到序遍歷,二維正序遍歷(2)一維只能先遍歷物品再遍歷背包,但是二維兩個順序都可。

public static void getBackValue(int[]value, int[] weight, int bagWeight){int num=value.length;int[]dp=new int[bagWeight+1];for(int j=weight[0];j<bagWeight;j++){dp[j]=value[0];}for(int i=1;i<num;i++){//從物品開始遍歷for(int j=bagWeight;j>=weight[i];j--){//要倒序遍歷dp[j]=Math.max(dp[j], dp[j-weight[i]]+value[i]);}}System.out.println(dp[bagWeight]);}   

416. 分割等和子集

給定一個只包含正整數的非空數組。是否可以將這個數組分割成兩個子集,使得兩個子集的元素和相等。

注意: 每個數組中的元素不會超過 100 數組的大小不會超過 200

示例 1:

  • 輸入: [1, 5, 11, 5]
  • 輸出: true
  • 解釋: 數組可以分割成 [1, 5, 5] 和 [11].

示例?2:

  • 輸入: [1, 2, 3, 5]
  • 輸出: false
  • 解釋: 數組不能分割成兩個元素和相等的子集.

提示:

  • 1 <= nums.length <= 200
  • 1 <= nums[i] <= 100

這道題希望能夠將一個數組拆成兩個子集a和b,使得a里面的元素和等于b里面的元素和。

沒有什么思路,直接看了代碼隨想錄。原來是01背包問題的變種。

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

注意題目描述中商品是不是可以重復放入。一個商品如果可以重復多次放入是完全背包,而只能放入一次是01背包。

要明確本題中我們要使用的是01背包,因為元素我們只能用一次。

回歸主題:首先,本題要求集合里能否出現總和為 sum / 2 的子集。(這一點是我沒有想到的)

那么來一一對應一下本題,看看背包問題如何來解決。

只有確定了如下四點,才能把01背包問題套到本題上來:

  • 背包的可容納的重量為sum / 2
  • 背包要放入的商品(集合里的元素)重量為元素的數值,價值也為元素的數值
  • 背包如果正好裝滿,說明找到了總和為 sum / 2 的子集。
  • 背包中每一個元素是不可重復放入

dp[j]表示:背包總容量是j,放進物品后,背包的最大價值為dp[j]

那么如果背包需要滿足的容量為target,當dp[target]==target時,背包就裝滿了

class Solution {/**背包的可容納的重量為sum / 2背包要放入的商品(集合里的元素)重量為元素的數值,價值也為元素的數值背包如果正好裝滿,說明找到了總和為 sum / 2 的子集。背包中每一個元素是不可重復放入*/public boolean canPartition(int[] nums) {int sum=0;for(int i=0;i<nums.length;i++){sum+=nums[i];}if(sum%2==1) return false;int target=sum/2;//weight[i]和value[i]都是nums[i],當前的bacWeight為targetint[] dp=new int[target+1];for(int j=nums[0];j<target;j++){dp[j]=nums[0];}for(int i=1;i<nums.length;i++){ //先遍歷物品for(int j=target;j>=nums[i];j--){//重量要倒序遍歷dp[j]=Math.max(dp[j], dp[j-nums[i]]+nums[i]);}}if(dp[target]==target) return true;return false;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/718212.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/718212.shtml
英文地址,請注明出處:http://en.pswp.cn/news/718212.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

隱式馬爾科夫算法

隱式馬爾科夫算法 隱式馬爾科夫算法概述算法使用HMM 模型參數設置HMM 模型分類1. Gaussian HMM2. Multinomial HMM3. GMM HMM 其他機器學習算法&#xff1a;機器學習實戰工具安裝和使用 隱式馬爾科夫算法概述 隱式馬爾科夫算法是一種用于處理時序數據的強大工具&#xff0c;其…

css通過calc動態計算寬度

max-width: calc(100% - 40px) .m-mj-status-drawing-info-data{ display: inline-block; margin: 10px; min-width: 200px; padding: 10px;border-radius: 10px; background: #ddd;max-width: calc(100% - 40px);word-wrap: break-word;white-space: pre-line;}我開發的chatg…

計算機二級(Python)真題講解每日一題:《字典字符查找》

描述???????????????????????????????????????????????????????????????????????????????????????????????????????????????? 在右側的答題模板中&#xf…

Crash 實例

1.spinlock原理 為了解決這個spinlock的不公平問題&#xff0c;linux 2.6.25內核以后&#xff0c;spinlock采用了一種"FIFO ticket-based"算法的spinlock機制&#xff0c;可以很好的實現先來先搶占的思想。具體的做法如下&#xff1a; (1)、spinlock的核心字段有ow…

C語言-柔性數組成員的使用

文章目錄 摘要柔性數組成員基本使用細節探究 零長度數組-定長數組-變長數組 摘要 本文先介紹柔性數組成員(flexible array member)的基本使用&#xff0c;然后介紹其內存結構。最后&#xff0c;補充了一些數組相關的其他概念。 柔性數組成員 基本使用 參考: 【C語言內功修煉…

[項目設計] 從零實現的高并發內存池(一)

&#x1f308; 博客個人主頁&#xff1a;Chris在Coding &#x1f3a5; 本文所屬專欄&#xff1a;[高并發內存池] ?? 前置學習專欄&#xff1a;[Linux學習] ? 我們仍在旅途 ? 目錄 前言 項目介紹 1.內存池 1.1 什么是內存池 池化技術 內存池 1.2 為什…

word使用bib添加參考文獻

文章目錄 安裝TexLive安裝bibtex4word使用在word中添加參考文獻使用bibtex4word在word中添加參考文獻設置參考文獻格式為畢業論文格式 參考 安裝TexLive 從下載地址下載鏡像iso文件texlive2023.iso雙擊打開iso鏡像文件運行 install-tl-windows.bat點擊安裝非常非常非常耐心地安…

Shell學習 - 2.20 Shell exit命令:退出當前進程

exit 是一個 Shell 內置命令&#xff0c;用來退出當前 Shell 進程&#xff0c;并返回一個退出狀態&#xff1b;使用$?可以接收這個退出狀態&#xff0c;這一點已在《Shell $?》中進行了講解。 exit 命令可以接受一個整數值作為參數&#xff0c;代表退出狀態。如果不指定&…

Linux命令-clock命令(用于調整 RTC 時間)

說明 clock命令用于調整 RTC 時間。 RTC 是電腦內建的硬件時間&#xff0c;執行這項指令可以顯示現在時刻&#xff0c;調整硬件時鐘的時間&#xff0c;將系統時間設成與硬件時鐘之時間一致&#xff0c;或是把系統時間回存到硬件時鐘。 語法 clock [--adjust][--debug][--dir…

客戶端/服務器協議是啥意思?

客戶端/服務器協議是指在網絡通信中&#xff0c;客戶端和服務器之間進行數據傳輸時所使用的規定。簡單來說&#xff0c;客戶端是用戶使用的設備&#xff0c;如電腦或手機&#xff0c;而服務器則是提供數據或服務的遠程計算機。當客戶端需要獲取數據或服務時&#xff0c;它會向服…

【RT-DETR有效改進】結合SOTA思想利用雙主干網絡改進RT-DETR(全網獨家創新,重磅更新)

一、本文介紹 本文給大家帶來的改進機制是結合目前SOTAYOLOv9的思想利用雙主干網絡來改進RT-DETR&#xff08;本專欄目前發布以來改進最大的內容&#xff0c;同時本文內容為我個人一手整理全網獨家首發 | 就連V9官方不支持的模型寬度和深度修改我都均已提供&#xff0c;本文內…

【活動】金三銀四,前端工程師如何把握求職黃金期

隨著春意盎然的氣息彌漫大地&#xff0c;程序員群體中也迎來了一年一度的“金三銀四”求職熱潮。這個時間段對于廣大前端工程師而言&#xff0c;不僅象征著生機勃發的新起點&#xff0c;更是他們職業生涯中至關重要的轉折點。眾多知名公司在這一時期大規模開啟招聘通道&#xf…

ChatGPT 4.0使用之論文閱讀

文章目錄 閱讀環境準備打開AskYourPDF進入主站 粗讀論文直接通過右側邊框進行提問選中文章內容翻譯或概括插圖的理解 總結 擁有了GPT4.0之后&#xff0c;最重要的就是學會如何充分發揮它的強大功能&#xff0c;不然一個月20美元的費用花費的可太心疼了&#xff08;家境貧寒&…

WP外貿營銷型網站模板

WordPress外貿獨立站主題 簡潔實用的WordPress外貿獨立站主題&#xff0c;適合時尚服裝行業搭建wordpress企業官網使用。 零件配件WordPress外貿建站模板 汽車行業零配件WordPress外貿建站模板&#xff0c;賣配件、零件的外貿公司可以使用的WordPress主題。 https://www.jia…

RocketMQ—消費者的兩種消費模式

RocketMQ—消費者的兩種消費模式 RocketMQ消息消費的模式分為兩種&#xff1a;負載均衡模式和廣播模式&#xff0c;負載均衡模式表示多個消費者交替消費同一個主題里面的消息&#xff1b;廣播模式表示每個每個消費者都消費一遍訂閱的主題的消息。 負載均衡模式 CLUSTERING 集…

vue2 element 實現表格點擊詳情,返回時保留查詢參數

先直觀一點&#xff0c;上圖 列表共5條數據&#xff0c;準備輸入Author過濾條件進行查詢 進入查看詳情頁&#xff0c;就隨便搞了個按鈕 啥都沒調啦 點擊返回后 一開始準備用vuex做這個功能&#xff0c;后來放棄了&#xff0c;想到直接用路由去做可能也不錯。有時間再整一套…

一篇文章了解和使用Map和Set(HashMap/TreeMap/HashSet/TreeSet)

[本節目標] *掌握HashMap/TreeMap/HashSet/TreeSet的使用 *掌握了解HashSet和HashSet背后的哈希原理和簡單的實現 1. 搜索樹 1.1 概念 二叉搜索樹又稱二叉排序樹,它或者是一顆空樹,或者是具有以下性質的二叉樹: 1.若它的左子樹不為空&#xff0c;則左子樹上所有節點的值都…

【一起學習Arcade】(2):Geometry函數

第二篇記錄下Geometry函數&#xff0c;相對于其它語言&#xff0c;Arcade對Geometry的支持是一大亮點&#xff0c;這使得它的上限被大大提高了。 三、Geometry函數 1、Angle【角度】 單位為度&#xff08;0-360&#xff09;&#xff0c;正北為90度&#xff0c;只考慮x-y平面。…

07OpenCV 圖像模糊

文章目錄 圖像掩膜操作模糊原理均值濾波高斯濾波中值濾波雙邊濾波算子代碼 圖像掩膜操作 圖像掩膜操作 模糊原理 Smooth/Blur是圖像處理中最簡單和常用的操作之一 使用操作的原因之一就是為了給圖像預處理時候減低噪聲 圖像噪聲是指存在于圖像數據中的不必要的或多余的干擾信…

RK3568開發筆記-qt程序運行報錯Failed to move cursor on screen

目錄 前言 一、qt程序運行報錯 二、異常解決 總結 前言 最近在進行 RK3568 平臺上的 Qt 程序開發時&