背包問題從入門到入土

我在這里介紹4種常見的背包問題,這里我想按易 --> 難程度從01背包,完全背包,分組背包,多重背包的順序介紹。(封面附在最后

一,01背包問題(后面三個背包問題的基礎

01背包問題就是從?n 物品中選取若干個體積為 v[i]?價值為 w[i] 的物品(同一個物品不能重復選),使容量為 V 的背包的價值最大。

1,01背包二維模板代碼

#include<bits/stdc++.h>
using ll = int;
using namespace std;
#define M 10005
ll dp[M][M] = { 0 };
int main() {int  n, v, w, V;  cin >> V >> n;for (int i = 1; i <= n; i++) {cin >> v >> w;for (int j = 1; j <= V; j++) {dp[i][j] = dp[i - 1][j];if (j >= v)dp[i][j] = max(dp[i][j], dp[i - 1][j - v] + w);}}cout << dp[n][V];return 0;
}

狀態定義:我們定義 dp[i][j] 為只考慮前 i 物品時,j 容量能裝下的物品價值總和的最大值

狀態轉移方程:dp[i][j] = max(dp[i][j], dp[i - 1][j - v] + w)

首先我們先繼承上一個狀態 dp[i][j] = dp[i - 1][j] ,然后判斷是否滿足狀態轉移的條件,即是否有空間加入當前的 i 物品,然后進行狀態轉移,其中狀態轉移的基礎是 dp[i - 1][j - v] ,即不包含物品 n (只包含前 n - 1 個物品),空間為 j - v 的最大價值。


2,01背包一維模板代碼

#include<bits/stdc++.h>
using ll = long long int;
using namespace std;
#define M 10005
ll dp[M] = { 0 };
int main() {int V, n, w, v;cin >> V >> n;for (int i = 1; i <= n; i++) {cin >> w >> v;for (int j = V; j >= w; j--)dp[j] = max(dp[j], dp[j - w] + v);}cout << dp[V];return 0;
}

狀態定義:我們定義 dp[j] 為只考慮前 i 個物品時,j 容量能裝下的物品價值總和的最大值

狀態轉移方程:dp[j] = max(dp[j], dp[j - w] + v)

這里的一維模板代碼是二維模板代碼的空間優化,這里需要被理解的主要有兩點,二維壓縮為一維的合理性以及為什么要倒序遍歷 j 。

1,二維壓縮為一維的合理性

我們的最終目的是為了得出最大價值,對于二維代碼來說就是?dp[n][V] ,那其他存儲空間有什么用呢,從代碼中我們我們不難看出我們在狀態繼承和狀態轉移中需要用到其他存儲空間(dp[i][j]), 那么一維能滿足這兩點嗎?

從狀態繼承來看,如果是一維,那么其實就不存在狀態繼承的問題,因為你在每個狀態上的操作都在同一行(只有一行);

從狀態轉移來看,既然狀態已經被繼承了,那么我們就完全可以在被繼承的狀態上(舊狀態)的基礎上轉移,這與二維背包的邏輯完全相同。

2,為什么要倒序遍歷 j?

倒序遍歷的根本原因在于01背包的特性:同一個物品不能重復選。

那換句話說,是不是如果不倒序遍歷 j ,也就是正序遍歷 j ,就會重復選同一個物品,答案是:是的。原因在于每個狀態都是從 dp[j - w] 也就是前 w 個狀態繼承過來的,那如果先更新前面的再更新后面的,就會發生狀態轉移的基礎 dp[j] 不再是(舊狀態)而是已經更新過的新狀態(這是一定的),外在表現就是重復選同一個物品(記住后面會考)。

二,完全背包問題

完全背包問題就是從?n 物品中每種選取若干個體積為 v[i]?價值為 w[i] 的物品(同一種物品能重復選且每種物品的數量無上限),使容量為 V 的背包的價值最大。

1,完全背包二維模板代碼

#include<bits/stdc++.h>
using namespace std;
#define M 10005
int dp[M][M] = { 0 };
int main() {int  n, V, v, w;cin >> n >> V;for (int i = 1; i <= n; i++) {cin >> v >> w;for (int j = 1; j <= V; j++) {dp[i][j] = dp[i - 1][j];if (j >= v)dp[i][j] = max(dp[i][j], dp[i][j - v] + w);}}cout << dp[n][V];return 0;
}

狀態定義:我們定義 dp[i][j] 為只考慮前 i 物品時,j 容量能裝下的物品價值總和的最大值

狀態轉移方程:dp[i][j] = max(dp[i][j], dp[i][j - v] + w)

首先我們先繼承上一個狀態 dp[i][j] = dp[i - 1][j], 然后判斷是否滿足狀態轉移的條件,即是否有空間加入當前的 i 物品,然后進行狀態轉移,其中狀態轉移的基礎是 dp[i][j - v] ,即包含物品 n (只包含前 n?種物品),空間為 j - v 的最大價值。

不難看出,完全背包模板代碼與01背包模板代碼的區別就在于狀態轉移的基礎,這也取決于完全背包問題的特性:同一種物品能重復選且每種物品的數量無上限。

2,完全背包一維模板代碼

#include<bits/stdc++.h>
using ll = long long int;
using namespace std;
#define M 10005
ll dp[M] = { 0 };
int main() {int n, V, w, v;cin >> n >> V;for (int i = 1; i <= n; i++) {cin >> w >> v;for (int j = w; j <= V; j++)dp[j] = max(dp[j], dp[j - w] + v);}cout << dp[V];return 0;
}

狀態定義:我們定義 dp[j] 為只考慮前 i 物品時,j 容量能裝下的物品價值總和的最大值

狀態轉移方程:dp[j] = max(dp[j], dp[j - w] + v)

這里的一維模板代碼是二維模板代碼的空間優化,這里需要被理解的主要也有兩點,二維壓縮為一維的合理性以及為什么要正序遍歷 j 。(其中二維壓縮為一維的合理性與01背包問題一致,這里不再贅述,詳見上文)

1,為什么要正序遍歷 j?

正序遍歷的根本原因也在于完全背包的特性:同一種物品能重復選且每種物品的數量無上限。

(詳見01背包)

三,分組背包問題

分組背包問題就是從?n 物品中每組選取一個體積為 v[i][k]?價值為 w[i][k]?的物品(任意一組物品能且僅能入選其中一個物品),使容量為 V 的背包的價值最大。

(二維模板代碼這里就不展示了,前面已經講過二維壓縮到一維的合理性和原理了,我們直接用最優的)

1,分組背包一維模板代碼

/*題目描述(hdu 1712)
輸入包含多個數據集。
每個數據集的第一行包含兩個正整數 N 和 M,N 表示課程數量,M 表示 ACboy 可用的天數。
接下來是一個矩陣 A[i][j],(1 <= i <= N <= 100,1 <= j <= M <= 100)。
A[i][j] 表示如果 ACboy 在第 i 門課程上花費 j 天,他將獲得價值為 A[i][j] 的收益。
當 N = 0 且 M = 0 時,輸入結束。
*/
#define _CRT_SECURE_NO_WARNINGS 1
#include<bits/stdc++.h>
#define M 105//提交以前記得改數據
using ll = long long int;
using namespace std;int main() {int n, m;while (~scanf("%d %d", &n, &m) && n && m) {int w[M][M];int dp[M] = { 0 };int v[M];for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) {cin >> w[i][j];v[j] = j;}	for (int i = 1; i <= n; i++) for (int j = m; j >= 0; j--) //總天數for (int k = 1; k <= j; k++) //付出的天數(k只是一個編號)if (j >= v[k])dp[j] = max(dp[j], dp[j - v[k]] + w[i][k]);cout << dp[m] << endl;}return 0;
}
/*樣例輸入
2 2
1 2
1 3
2 2
2 1
2 1
2 3
3 2 1
3 2 1
0 0
*//*樣例輸出
3
4
6
*/

狀態定義:我們定義 dp[j] 為只考慮前 i 物品時,j 容量能裝下的物品價值總和的最大值

狀態轉移方程:dp[j] = max(dp[j], dp[j - v[k]] + w[i][k])

這里的一維模板代碼是二維模板代碼的空間優化,這里需要被理解的主要有三點:二維壓縮為一維的合理性(這里不再贅述),為什么要倒序遍歷 j 以及為什么要三層循環。

1,為什么要倒序遍歷 j?

單從這一點來看分組背包一維代碼和01背包一維代碼類似,那是什么導致了這個 “類似” 呢?

我們還是從兩種問題的特性出發:

01背包:同一個物品不能重復選

分組背包:任意一組物品能且僅能入選其中一個物品

大家應該發現了,“一個” 是關鍵,那為什么這一特性會導致倒序呢?

(詳見01背包)

2,為什么要三層循環

這還是取決于分組背包的特性:每組一個。

既然每組只能選一個,我們又要價值最大,那我們就要在容量的范圍內盡可能選最大的,怎么選最大呢,最簡單的思路就是應用選擇排序的原理:打擂臺(價值更大的當擂主,然后一直守擂,知道遇到更大的挑戰者),事實上我們在這里也是這么做的,而這一操作又自成一層循環,加上本來的兩層,2+1=3。

四,多重背包

多重背包問題就是從?n 物品中每種選取若干個體積為 v?價值為 w?的物品(同一種物品能重復選,每種物品的數量有限),使容量為 V 的背包的價值最大。

思路1:

在展示最優代碼之前我先說一種簡單的方法,把多重背包問題看成是01背包問題,畢竟多重背包問題物品是有限的而且還沒有選取限制,實際上01背包問題是特殊的多重背包問題(每種物品只有一個)。但是特殊情況的解法并不一定能適用于普遍情況,當每種問題的物品太多就會TLE,所以這種方法不推薦用,也就圖一樂。

1,多重背包一維模板代碼

#include<bits/stdc++.h>
using ll = long long int;
using namespace std;
#define M 100005
int dp[M] = { 0 };
int main() {int V, n;cin >> V >> n;for (int i = 0, v, w, s; i < n; i++) {cin >> v >> w >> s;for (int k = 1; s; s -= k, k *= 2) {//把 s 進行二進制拆分,類似于 st 表k = min(s, k);for (int j = V; j >= k * v; j--) dp[j] = max(dp[j], dp[j - k * v] + k * w);}}cout << dp[V];return 0;
}

思路2:

這個代碼一看就知道我為什么要把它放到最后了,要弄懂這個代碼需要一定的 st 表的基礎,那這時候就有兄弟要問了,有沒有不會 st 表也能理解的方法呢,有的兄弟有的,二進制大家都懂吧,任意一個數都能用二進制表示,也就是由1,2,4,8,16,32,64...這些數中的一部分組成的,那么我們還是用沿用思路1,只是在其中加一步

for (int k = 1; s; s -= k, k *= 2) {k = min(s, k);...
}

這一步其實就是把一種問題的所有物品進行整合,整合成 1 * v,2 * v,4 * v,8 * v,16 * v,32 * v,64 * v...,然后從 1 * v 這個部分開始倒序刷表(為什么倒序我就不贅述了),這樣一來不管面對多大的體積 j,我們總有一種合適的組合方式適合 j 。這樣一來我們只要預處理好?1 * v,2 * v,4 * v,8 * v,16 * v,32 * v,64 * v...我們就能以對數階的復雜度進行刷表。

寫完之后博主要燃盡了,給個贊回回血吧,我先吸吸貓(轉載)

羅小黑亂入(都是貓貓為什么不可以)

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

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

相關文章

Leetcode 18 java

?????1???????141. 環形鏈表1 題目 ?????1???????141. 環形鏈表 給你一個鏈表的頭節點 head &#xff0c;判斷鏈表中是否有環。 如果鏈表中有某個節點&#xff0c;可以通過連續跟蹤 next 指針再次到達&#xff0c;則鏈表中存在環。 為了表示給定鏈表…

Linux 正則表達式詳解(基礎 + 擴展 + 實操)

Linux 正則表達式詳解&#xff08;基礎 擴展 實操&#xff09; 正則表達式&#xff08;Regular Expression&#xff0c;簡稱 RE&#xff09;是 Linux 文本處理的核心工具&#xff0c;用于定義字符匹配模式&#xff0c;配合 grep、sed、awk 等工具可實現文本過濾、查找、替換等…

Json-rpc通信項目(基于C++ Jsoncpp muduo庫)

一、介紹RPC RPC&#xff08;Remote Procedure Call&#xff09;遠程過程調用&#xff0c;一種通過網絡從遠程計算器上請求服務&#xff0c;而不需要了解底層網絡通信細節&#xff0c;RPC可以使用多種網絡協議進行通信&#xff0c;并且在TCP/IP網絡四層模型中跨越了傳輸層和應…

RL【9】:Policy Gradient

系列文章目錄 Fundamental Tools RL【1】&#xff1a;Basic Concepts RL【2】&#xff1a;Bellman Equation RL【3】&#xff1a;Bellman Optimality Equation Algorithm RL【4】&#xff1a;Value Iteration and Policy Iteration RL【5】&#xff1a;Monte Carlo Learnin…

Redis是什么?一篇講透它的定位、特點與應用場景

Redis是什么&#xff1f;一篇講透它的定位、特點與應用場景 1. Redis的定義與核心概念 1.1 什么是Redis&#xff1f; Redis&#xff08;Remote Dictionary Server&#xff09; 是一個開源的、基于內存的數據結構存儲系統&#xff0c;可以用作數據庫、緩存和消息代理。Redis由…

一款免費開源輕量的漏洞情報系統 | 漏洞情報包含:組件漏洞 + 軟件漏洞 + 系統漏洞

工具介紹 bug_search一款免費開源輕量的漏洞情報系統 基于python3 Amis2.9 開發&#xff0c;僅依賴Flask,requests&#xff0c;無需數據庫&#xff0c;Amis是百度開源的低代碼前端框架漏洞情報包含&#xff1a;組件漏洞 軟件漏洞 系統漏洞 增加郵件發送消息報警功能增加釘釘…

詳解在Windows系統中生成ssl證書,實現nginx配置https的方法

目錄一、下載安裝OpenSSL二、證書生成三、修改nginx配置總結Nginx 是一個高性能的HTTP和反向代理web服務器&#xff0c;在進行web項目開發時&#xff0c;大多都是使用nginx對外提供web服務。HTTPS &#xff08;全稱&#xff1a;Hypertext Transfer Protocol Secure [5]&#xf…

AI視覺算法中的OpenCV API (二)

視頻寫入 (FourCC, VideoWriter)? 1. VideoWriter_fourcc - 視頻編碼器四字符代碼 # OpenCV 3.x, 4.x fourcc cv2.VideoWriter_fourcc(M,J,P,G)fourcc cv2.VideoWriter_fourcc(*H264)fourcc cv2.VideoWriter_fourcc(*MJPG) ?FourCC?&#xff1a; 代表 ?Four ?Charac…

分享| 2025年版AIGC數字人實驗室解決方案教學資源解析

AIGC數字人實驗室解決方案構建了涵蓋基礎層、平臺環境層與資源層的多層次教學架構&#xff0c;依托150平方米的實體空間與60人并行授課的規模化支持&#xff0c;為學生提供了技術實踐與創新的高效平臺。其教學資源體系覆蓋AIGC文本生成、圖像生成、數字人應用與智能體開發四大核…

內存大(巨)頁

一、大&#xff08;巨&#xff09;頁 大&#xff08;巨&#xff09;頁&#xff0c;很好理解&#xff0c;就是的大的頁。說這個大頁前&#xff0c;得先把計算機中內存的管理簡單說明一下&#xff0c;否則可能對于一些新手或者把操作系統中內存管理的方法的開發者不太友好。最早的…

langgraph astream使用詳解

langgraph中graph的astream&#xff08;stream&#xff09;方法分別實現異步&#xff08;同步&#xff09;流式應答&#xff0c;在langgraph-api服務也是核心方法&#xff0c;實現與前端的對接&#xff0c;必須要把這個方法弄明白。該方法中最重要的參數是stream_mode&#xff…

【C++】模板進階:非類型參數、模板特化與分離編譯

目錄 1. 非類型模板參數 2. 模板的特化 3. 分離編譯 1. 非類型模板參數 模板參數分類類型形參與非類型形參。 類型形參即&#xff1a;出現在模板參數列表中&#xff0c;跟在class或者typename之類的參數類型名稱。 非類型形參&#xff0c;就是用一個常量作為類(函數)模板…

棧-1047.刪除字符串中的所有相鄰重復項-力扣(LeetCode)

一、題目解析 1、反復執行重復項刪除操作 2、s僅由小寫英文字母組成 二、算法原理 該題并不難&#xff0c;難的是能不能想到用棧這個數據結構解題 解法&#xff1a;棧模擬 橫著看起來不好理解&#xff0c;我們把它豎起來&#xff0c;是不是和消消樂很類似&#xff0c;兩兩消…

【每日算法】移除元素 LeetCode

雙指針方法是解決數組或鏈表問題中非常高效的技巧之一&#xff0c;尤其適用于原地修改數組或減少時間復雜度的場景。以下是對雙指針方法的詳細講解。1. 雙指針方法的核心思想雙指針方法通常使用兩個指針&#xff08;或索引&#xff09;在數組或鏈表中協同工作&#xff0c;通過一…

Android 項目:畫圖白板APP開發(六)——分頁展示

本篇將介紹如何為我們的畫板應用添加分頁展示功能&#xff0c;讓用戶可以創建多個畫布并在它們之間輕松切換。這章沒有啥知識點的講解&#xff0c;主要介紹一下每頁保存的數據結構是什么樣的。 一、ListView 多頁數據的管理我們使用ListView。之前有文章講過ListView這里就不多…

智能眼鏡產品成熟度分析框架與評估

引言 當前(2025年9月12日),智能眼鏡(Smart Glasses)市場正處于快速演進階段,從早期的新奇設備向主流消費電子轉型。AI整合、AR顯示和多模態交互的進步推動了這一轉變。根據最新數據,2025年AI眼鏡發貨量預計達686萬臺,同比增長265%,全球市場規模從2024年的約19.3億美元…

(網絡編程)網絡編程套接字 UDP的socket API 代碼解析

網絡編程基礎 為什么需要網絡編程?--豐富的網絡資源 用戶在瀏覽器中,打開在線視頻網站,如優酷看視頻,實質是通過網絡,獲取到網絡上的一個視頻資源。與本地打開視頻文件類似,只是視頻文件這個資源的來源是網絡。 相比本地資源來說,網絡提供了更為豐富的網絡資源:所謂的網絡資源…

【STM32】狀態機(State Machine)

這篇博客介紹 狀態機&#xff08;State Machine&#xff09;&#xff0c;適合用于嵌入式開發、驅動開發、協議解析、按鍵識別等多種場景。 一、什么是狀態機&#xff08;State Machine&#xff09;&#xff1f; 狀態機&#xff08;State Machine&#xff09;是一種用于描述系統…

深度學習在離崗檢測中的應用

離崗檢測技術正逐步成為現代企業精細化管理和安全生產的重要工具。這項基于計算機視覺和人工智能的應用&#xff0c;通過自動化、實時化的監測方式&#xff0c;有效提升了工作紀律性和運營效率&#xff0c;為項目管理者和企業提供了創新的監管解決方案。在許多工作場景中&#…

Spring緩存(二):解決緩存雪崩、擊穿、穿透問題

1. 緩存穿透問題與解決方案 1.1 什么是緩存穿透 緩存穿透是指查詢一個不存在的數據&#xff0c;由于緩存中沒有這個數據&#xff0c;每次請求都會直接打到數據庫。 如果有惡意用戶不斷請求不存在的數據&#xff0c;就會給數據庫帶來巨大壓力。 這種情況下&#xff0c;緩存失去了…