動態規劃(6)——01背包問題

歡迎來到博主的專欄:算法解析
博主ID:代碼小號

文章目錄

    • 牛客網——【模板】01背包
      • 題目解析
      • 題目1算法原理
      • 題目1題解代碼。
      • 問題2算法原理
      • 問題2題解代碼
      • 01背包問題的滾動數組優化

牛客網——【模板】01背包

題目解析

在這里插入圖片描述
關于I/O相關的東西博主就不多贅述了,我們以示例1為例,當前地上有3個物體,背包的體積只有5。物體1的體積是2,價值為10,物體2的體積是4,價值為5,物體3的體積是1,價值為4。(1)要求我們在不超過背包體積的情況下,裝下的物體的最大價值。(2)要求我們求出當背包正好裝滿時,能裝下的最大價值。
在這里插入圖片描述

對于問題1,由于我們要盡可能的追求最大價值,因此只要背包能裝下東西就一定要裝下:示例1有下面兩種方案。選擇價值最大的方案
在這里插入圖片描述

對于問題2,只有一種方案能讓背包裝滿,因此雖然價值不是最大的,但是依舊是最終答案
在這里插入圖片描述

題目1算法原理

對于問題1,我們要抽象出背包問題的兩個重要屬性。1、可挑選的物品有限制,2、要求挑選出物品的最大價值,那么我們的狀態表示就要涵蓋這兩個方面。

我們將物品進行從1開始編號,如下
在這里插入圖片描述
我們規定,dp[i][j]表示:在[1,i]號物品中進行挑選,物體的總體積不超過j的最大價值。為什么要假設是這個狀態表示?首先根據上面的分析,我們知道狀態表示要涵蓋對選取物體的限制,同時也要確定最大價值,其二則是根據題目要求,題目要求我們在n個物品中挑選體積不超過背包體積的最大價值,而正好我們的狀態表示符合這個要求。

那么如何判斷我們的狀態表示正確與否呢?我們可以先嘗試用這個狀態表示來推導一下狀態轉移方程,如果狀態轉移方程推導的不是很順利。那么就要嘗試更換一個狀態表示了。

回到dp[i][j]的狀態表示。在[1,i]中挑選物品,對于每一個i,可以將所有的可能的情況分成兩種,一種是不將物體i裝進背包的情況,一種是將物體i裝進背包的情況。

我們根據這兩個情況推導狀態專題方程。
在這里插入圖片描述
如果我們選擇不將i裝入背包,那么此時我們就要在剩下的物品中,挑選總體積不超過j的多個物品,但是我們由于我們的dp[i][j]需要求的是最大值,那么也就說明,在剩下的物品中挑選總體積不超過j的情況,也必須是最大價值,即dp[i-1][j]。那么此時dp[i][j]=dp[i-1][j]。


如果我們將第i個物體放入背包,那么為了追求最大價值,我們需要在[1,i-1]當中挑選剩下的物品,但是,由于此時背包當中已經放入一個i了,那么剩下的可容納體積則是j-v[i]。因此該情況下的dp[i][j]=dp[i-1][j-v[i]]+w[i]。由于我們要求出的是最大值,因此狀態轉移方程如下:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i])。但是要注意下標的問題,j-v[i]是可能小于0的,因此只有j-v[i]<=0的時候,我們就認為dp[i-1][j-v[i]]+w[i]的值為0。

在這里插入圖片描述

接下來就是初始化問題,由于狀態轉移方程涉及i-1這個操作,因此我們不能讓i=0,因此我們要對其進行初始化。

如果i0,j0,則說明在0個物體當中挑選體積不超過0的最大價值,由于此時沒有物體可選,因此背包價值為0,即dp[0][0]=0
如果i=0,j>0,則說明在0個物體當中挑選體積不超過j的最大價值,同樣的沒有物體可選,因此背包價值為0.dp[0][j]=0

由于題目中會輸入n個物體,因此v[i]和w[i]的下標是[0,n-1]范圍內,但是我們的dp表的返回值是dp[n][V]。即表示在n個物體當中組合出體積不超過V的最大價值。所以dp表的是一個(n+1)*(V+1)大小的數組。此時我們要注意dp表的狀態轉移方程與v[i]和w[i]的下標映射關系。即:dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i-1]]+w[i-1])

題目1題解代碼。

int main() {int n,V;cin>>n>>V;vector<int> v(n);//物體體積表vector<int> w(n);//物體價格表for(int i=0;i<n;i++) cin>>v[i]>>w[i];vector<vector<int>> dp(n+1,vector<int>(V+1));for(int i=1;i<n+1;i++){for(int j=0;j<V+1;j++){dp[i][j]=dp[i-1][j];//不選i的情況下的最大值if(j-v[i-1]>=0) dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i-1]]+w[i-1]);//選i情況下的最大值,前提是合法}}cout<<dp[n][V]<<endl;
}

問題2算法原理

由于問題2要求是在裝滿背包的情況下,裝取物品的最大價值。 那么我們就可以將狀態表示修改一下:dp[i][j]表示,在前i個物品當中進行挑選,當挑選的物品恰好總體積為J時,背包的最大價值。

那么我們可以開始推導狀態轉移方程了,對于任意i,我們可以將所有的可能的枚舉情況,分成兩種,一種是選擇i的情況,一種是不選i的情況。因此我們需要推導出這兩種情況下dp[i][j]的最大值。

對于不選i的情況,此時我們可以在剩下的i-1個物品中挑選恰好體積等于j的最大值,那么這段描述不是符合dp[i-1][j]的狀態表示?因此狀態轉移方程為:dp[i-1][j]。

對于選i的情況,此時由于物品i已經被選進背包了,此時背包容量只剩j-v[i]。物品還剩下i-1個可以挑選,因此我們需要在剩下的i-1個物品進行挑選,使其總體積恰好為j-v[i]的情況下的最大價值,那么這段描述的狀態表示就是dp[i-1][j-v[i]]。由于物品i已經被選上了,因此選i的背包價值為: dp[i][j]=dp[j-v[i]]+w[i]

但是有一個問題出現了,如果dp[i-1][j-v[i]]有沒有可能不存在?當然是有可能的,因為剩下的i-1個物體很有可能怎么湊,都湊不出j-v[i]這個體積,那么相應的,dp[i][j]的值也就不存在了。那么我們就要思考一個問題了,對于非法的狀態表示(即無法湊滿總體積為j的物品),該用什么值來表示呢?有人說既然背包沒有東西,那不就是沒有價值。因此用0來表示。但是我們再思考一下,0這個值一定是非法的嗎?在前面討論初始化的時候,博主是不是說過,當i0時,j0時,此時有0個物體可挑選,需要讓挑選物品的總體積恰好為0。這個狀態明明是合法的,而且由于此時什么也沒挑,因此背包的最大價值為0,因此0不是非法狀態下的取值。我們可以使用-1來表示非法狀態下的表示。

因此狀態轉移方程為:dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i])(dp[i-1][j-v[i]]合法的情況下(不為-1))。

接下來就是初始化的問題,i0時,j0時合法,因此背包價值為0,dp[0][0]=0.當i==0,j>0時,此時無法湊成總體積恰好為j的情況,因此是非法的情況,即dp[0][j]=-1。那么最后一個細節就是體積表和價值表與dp表之間的映射問題了。

問題2題解代碼

#include <iostream>
#include <vector>
using namespace std;int main() {int n,V;cin>>n>>V;vector<int> v(n);//物體體積表vector<int> w(n);//物體價格表for(int i=0;i<n;i++) cin>>v[i]>>w[i];vector<vector<int>> dp(n+1,vector<int>(V+1));for(int i=1;i<n+1;i++){for(int j=0;j<V+1;j++){dp[i][j]=dp[i-1][j];//不選i的情況下的最大值if(j-v[i-1]>=0) dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i-1]]+w[i-1]);//選i情況下的最大值,前提是合法}}cout<<dp[n][V]<<endl;//問題1的結果dp.resize(n+1,vector<int>(V+1));//重新復用一下dp表for(int j=1;j<V+1;j++) dp[0][j]=-1;for(int i=1;i<n+1;i++){for(int j=0;j<V+1;j++){dp[i][j]=dp[i-1][j];//選i且合法的情況if(j-v[i-1]>=0&&dp[i-1][j-v[i-1]]!=-1) dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i-1]]+w[i-1]);//}}cout<<(dp[n][V]==-1?0:dp[n][V])<<endl;//問題2的結果
}

01背包問題的滾動數組優化

根據狀態轉移方程dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i-1]]+w[i-1]);對于任意一個位置的dp[i][j],其取值返回都只在上一行當中。
在這里插入圖片描述
(寫到這里博主的截圖工具有點用不了了,但是又是因為在學校機房寫的,因此不想關機重寫hh,那就用截圖鍵湊合看吧)

我們可以看到實際上只需要兩行的數組,只要下面一行填完了,就讓下面一行去作為上面一行繼續更新。那么就可以實現空間優化。
在這里插入圖片描述
實際上這個工作只需要一行數組就能完成。如下:
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

由于我們對于任意一個dp[i][j],我們都只會用到上一行,以及上一行當中左邊的某一個值。因此如果我們之開一行數組,然后從右往左遍歷dp。這樣dp表就可以僅使用一維的情況下,完成整個過程,節省了空間開銷。

代碼如下:

#include <iostream>
#include <vector>
using namespace std;int main() {int n,V;cin>>n>>V;vector<int> v(n);//物體體積表vector<int> w(n);//物體價格表for(int i=0;i<n;i++) cin>>v[i]>>w[i];vector<int> dp(V+1);//只需要一行dpfor(int i=1;i<n+1;i++){//注意雖然dp表只有一行,但是狀態轉移方程并沒有改變,因此i不能刪除for(int j=V;j>=v[i-1];j--){dp[j]=max(dp[j],dp[j-v[i-1]]+w[i-1]);//選i情況下的最大值,前提是合法}}cout<<dp[V]<<endl;//問題1的結果dp.resize(V+1);//重新復用一下dp表for(int j=1;j<V+1;j++) dp[j]=-1;for(int i=1;i<n+1;i++){for(int j=V;j>=v[i-1];j--){//選i且合法的情況if(dp[j-v[i-1]]!=-1) dp[j]=max(dp[j],dp[j-v[i-1]]+w[i-1]);//}}cout<<(dp[V]==-1?0:dp[V])<<endl;//問題2的結果
}

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

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

相關文章

TQTT_KU5P開發板教程---實現流水燈

文檔實現功能介紹 本文檔是學習本開發板的基礎&#xff0c;通過設置計數器使led0到led7依次閃爍&#xff0c;讓用戶初步認識vivado基本的開發流程以及熟悉項目的創建。本開發板的所有教程所使用的軟件都是vivado2024.1版本的。可以根據網上的教程下載與安裝。 硬件資源 此次教程…

Spring 中的 @Cacheable 緩存注解

1 什么是緩存 第一個問題&#xff0c;首先要搞明白什么是緩存&#xff0c;緩存的意義是什么。 對于普通業務&#xff0c;如果要查詢一個數據&#xff0c;一般直接select數據庫進行查找。但是在高流量的情況下&#xff0c;直接查找數據庫就會成為性能的瓶頸。因為數據庫查找的…

SEER: Self-Aligned Evidence Extraction for Retrieval-AugmentedGeneration

一、動機 如何從檢索到的段落中提取證據&#xff0c;以降低計算成本并提升最終的RAG性能&#xff0c;然而這一問題仍然具有挑戰性。 現有方法 嚴重依賴于基于啟發式的增強&#xff0c;面臨以下幾個問題&#xff1a; &#xff08;1&#xff09;由于手工制作的上下文過濾&…

毫米波測試套裝速遞!高效賦能5G/6G、新材料及智能超表面(RIS)研發

德思特&#xff08;Tesight&#xff09;作為全球領先的測試測量解決方案提供商&#xff0c;始終致力于為前沿技術研發提供高精度、高效率的測試工具。 針對毫米波技術在高頻通信、智能超表面&#xff08;RIS&#xff09;、新材料等領域的快速應用需求&#xff0c;我們推出毫米…

三維激光測量助力企業檢測效率提升3倍

智能制造與數字化浪潮席卷下&#xff0c;三維掃描技術已成為工業檢測領域不可或缺的工具。面對傳統檢測手段的精度瓶頸與效率局限&#xff0c;三維掃描儀&#xff0c;以毫米級精度、非接觸式測量與超高速掃描三大核心優勢&#xff0c;為汽車制造、航空航天、消費電子等行業的品…

SQL:Normalization(范式化)

目錄 Normalization&#xff08;范式化&#xff09; 為什么需要 Normalization&#xff1f; &#x1f9e9; 表格分析&#xff1a; 第一范式&#xff08;1NF&#xff09; 什么是第一范式&#xff08;First Normal Form&#xff09;&#xff1f; 第二范式&#xff08;2NF&am…

#MES系統運維問題分析思路

一套適用于90% MES運維現場問題的排查分析思維模型&#xff0c;叫做&#xff1a; &#x1f50d; MES系統問題分析七步法&#xff08;現場實戰適用&#xff09; ? 第一步&#xff1a;明確問題現象&#xff08;What&#xff09; 問題要說清楚&#xff0c;“不能操作”這種模糊描…

達夢數據庫-學習-18-ODBC數據源配置(Linux)

一、環境信息 名稱值CPU12th Gen Intel(R) Core(TM) i7-12700H操作系統CentOS Linux release 7.9.2009 (Core)內存4G邏輯核數2DM版本1 DM Database Server 64 V8 2 DB Version: 0x7000c 3 03134284194-20240703-234060-20108 4 Msg Versi…

js 效果展示 拿去練手

自學完整功能&#xff0c;拿去練手。 鼠標移動放大 通過網盤分享的文件&#xff1a;圖片放大 鏈接: https://pan.baidu.com/s/1w8SjtKi4kUNDnZtRDfYMeQ?pwd95p6 提取碼: 95p6 通過網盤分享的文件&#xff1a;圖片動畫效果 鏈接: https://pan.baidu.com/s/1Pjphx-Cc4HQQNNujr…

使用 TFIDF+分類器 范式進行企業級文本分類(二)

1.開場白 上一期講了 TF-IDF 的底層原理&#xff0c;簡單講了一下它可以將文本轉為向量形式&#xff0c;并搭配相應分類器做文本分類&#xff0c;且即便如今的企業實踐中也十分常見。詳情請見我的上一篇文章 從One-Hot到TF-IDF&#xff08;點我跳轉&#xff09; 光說不練假把…

硬件設計-MOS管快速關斷的原因和原理

目錄 簡介&#xff1a; 來源&#xff1a; MOS管快關的原理 先簡單介紹下快關的原理&#xff1a; 同電阻時為什么關斷時間會更長 小結 簡介&#xff1a; 本章主要介紹MOS快速關斷的原理和原因。 來源&#xff1a; 有人會問&#xff0c;會什么要求快速關斷&#xff0c;而…

Linux進階命令

目錄 一、touch 1. 基本語法 2. 常用選項 二、which 1. 基本語法 2. 主要功能 3. 常用選項 三、find 1. 基本語法 2. 常用選項和表達式 四、more 1. 基本語法 2. 常用操作 3. 對比 more 和 less 五、grep 1. 基本語法 2. 常用選項 六、wc 1. 基本語法 2. 常…

阿里云實時計算Flink版產品體驗測評

阿里云實時計算Flink版產品體驗測評 什么是阿里云實時計算Flink應用場景實時計算Flink&自建Flink集群性價比開發效率運維管理企業安全 場景落地 什么是阿里云實時計算Flink 實時計算Flink大家可能并不陌生&#xff0c;在實時數據處理上&#xff0c;可能會有所接觸&#xf…

用戶登錄不上linux服務器

一般出現這種問題&#xff0c;重新用root用戶修改lsy用戶的密碼即可登錄&#xff0c;但是當修改了還是登錄不了的時候&#xff0c;去修改一個文件用root才能修改&#xff0c; 然后在最后添加上改用戶的名字&#xff0c;例如 原本是只有user的&#xff0c;現在我加上了lsy了&a…

Android Jetpack架構組件——用Compose工具包構建基本的布局

推薦文章 構建基本布局 | Android Basics Compose - First Android app | Android Developers 向 Android 應用添加圖片 | Android Developers

SLAM(七)-卡爾曼濾波

SLAM&#xff08;七&#xff09;-卡爾曼濾波 一、卡爾曼濾波(KF)二、擴展卡爾曼濾波(EKF)三、誤差狀態卡爾曼濾波(ESKF) 參考《概率機器人》、《Principles of GNSS&#xff0c;lnertial and Multisensor lntegrated Navigation Systems (Second Edition)》 一、卡爾曼濾波(KF)…

Electron 應用太重?試試 PakePlus 輕裝上陣

Electron 作為將 Web 技術帶入桌面應用領域的先驅框架&#xff0c;讓無數開發者能夠使用熟悉的 HTML、CSS 和 JavaScript 構建跨平臺應用。然而&#xff0c;隨著應用規模的擴大&#xff0c;Electron 應用的性能問題逐漸顯現——內存占用高、啟動速度慢、安裝包體積龐大&#xf…

Vue.js組件安全工程化演進:從防御體系構建到安全性能融合

——百萬級流量場景下的安全組件架構與源碼級解決方案 文章目錄 總起&#xff1a;安全工程化的組件革命 分論&#xff1a; 一、現存組件架構的七宗罪與安全改造路徑   1.1 組件生態安全赤字現狀   1.2 架構級安全缺陷深度剖析   1.3 性能與安全的死亡螺旋 二、百萬級…

MCP+cursor使用嘴操作數據庫(不用編寫SQL語句實現CURD)

文章目錄 1.如何進行相關配置2.如何添加MCP server3.如何進行相關的操作3.0數據的查詢3.1數據的插入3.2數據的修改3.3多表連接查詢 1.如何進行相關配置 這個跟昨天的高德地圖的配置非常的相似&#xff0c;因此這個地方我就不進行過多的這個說明了&#xff0c;就是新加一個全聚…

效率工具- git rebase 全解

一、前言 對于git rebase 一直不太了解,這幾天想著提高下git提交質量,就發現了這個好用的指令,順便記錄一下,好加深記憶 貼出官方文檔以便大家進一步學習 Git 二、rebase是作用 rebase 官方解釋為變基,可以理解為移動你的分支根節點,維護一個更好的提交記錄。rebase把你當前…