藍橋杯備賽 背包問題

背包問題

![[背包問題.png]]

01背包

1.題意概要:有 n n n個物品和一個容量為 V V V的背包,每個物品有重量 w i w_i wi?和價值 v i v_i vi??兩種屬性,要求選若干物品放入背包使背包中物品的總價值最大且背包中物品的總重量不超過背包的容量
要求:每個物品只能拿一次,所以每個物品只有拿或不拿兩種狀態,故稱01背包
2.狀態:dp[i][j]表示到第i個物品為止(不一定拿),到容量j為止,背包的最大價值
狀態轉移方程:dp[i][j]=max(dp[i-1][j],dp[i-1][j-w]+v)(注意先判斷j>=w)(不關心有沒有拿,只關心最大價值)
最終狀態:dp[n][V]

小明的背包1

學習:
(1)模版題,可以不用開w[i],v[i]數組記錄,反正是一個一個物品枚舉的
代碼:

#include <bits/stdc++.h>using namespace std;const int N=1e2+10,V=1e3+10;
int n,vol,dp[N][V];int main(){ios::sync_with_stdio(false);cin>>n>>vol;for(int i=1;i<=n;i++){int w,v;cin>>w>>v;for(int j=1;j<=vol;j++){//判斷是否越界,即能不能放下第i個物品 if(j>=w)	dp[i][j]=max(dp[i-1][j],dp[i-1][j-w]+v);else dp[i][j]=dp[i-1][j];}}cout<<dp[n][vol];return 0;
}
01背包一維滾動數組優化

學習:
(1)二維數組的第一維度dp[i]dp[i-1]本質就是第i-1層的值來轉移給第i層,就是用遍歷i來實現的,因此可以優化為滾動一維數組,讓前面的舊值轉移給后面的新值,所以一個維度dp[j]就夠了,表示背包容量到j為止的最大價值,狀態轉移方程:
dp[j]=max(dp[j],dp[j-w]+v)
(2)背包容量倒序遍歷,從 V V V開始 w i w_i wi?,原因如下圖:
![[01背包優化圖.png]]
黃色塊為舊值,綠色塊為新值,左側二維dp[i]的綠色新值都是由dp[i-1]黃色舊值轉移而來,所以背包容量從前往后或從后往前無所謂,而右側一維只能是黃色舊值轉移給綠色新值,所以從后往前遍歷,遇到個新值dp[j],從前面的舊值dp[j-w]轉移過來,如果從前往后遍歷,會出現dp[j-w2]dp[j-w2-w1]轉移過一次,變成新值(不再是舊值dp[j-w2]),dp[j]新值dp[j-w2]轉移過來,相當于加上了兩次物品,例如:

        w    v
物品
1       1    2
j                0  1  2
dp[j](從前往后)   0  2  4(出現問題,考慮了兩次物品1)
dp[j](從后往前)   0  2  2    
小明的背包1優化代碼
#include <bits/stdc++.h>using namespace std;
const int V=1e3+10;
int n,vol,dp[V];int main(){ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>vol;for(int i=1;i<=n;i++){int w,v;cin>>w>>v;for(int j=vol;j>=w;j--){ //倒序遍歷 dp[j]=max(dp[j],dp[j-w]+v);}}cout<<dp[vol];return 0;
}
背包與魔法

學習:
(1)這題是01背包的變體,因為多了一個是否使用魔法,且最多使用一次,就相當于狀態dp多了一個維度的變量(這個思想很重要),就有兩大種狀態dp[j][0],dp[j][1],三種狀態轉移:1.不加入物品2.加入物品不使用魔法3.加入物品使用魔法,以及相應的狀態轉移方程

//不使用魔法,一般的一維01背包
dp[j][0]=max(dp[j][0],dp[j-w][0]+v); 
dp[j][1]=max(dp[j][1],dp[j-w][1]+v);
//使用魔法
if(j>=(w+k))	dp[j][1]=max(dp[j][1],dp[j-(w+k)][0]+2*v);//精華所在

(2)狀態轉移方程在同一個反向遍歷j下轉移,表示對當前物品的轉移,因為j最小到w,所以第二種狀態轉移方程要有個判斷j>=(w+k)
(3)求多個元素的最大值,中間套個列表({})即可
maxn=max({1,2,3,4})
代碼:

#include <bits/stdc++.h>using namespace std;
typedef long long ll;
const int N=2e3+10,M=1e4+10;
int n,m,k;
ll dp[M][2]; //第一維度為背包重量j,第二維度表示是否使用魔法 int main(){ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>m>>k;for(int i=1;i<=n;i++){	int w,v;cin>>w>>v;	for(int j=m;j>=w;j--){//不使用魔法,一般的一維01背包dp[j][0]=max(dp[j][0],dp[j-w][0]+v); dp[j][1]=max(dp[j][1],dp[j-w][1]+v);//使用魔法if(j>=(w+k))	dp[j][1]=max(dp[j][1],dp[j-(w+k)][0]+2*v); }}cout<<max(dp[m][0],dp[m][1]);return 0;
}

完全背包

(1)特征:每個物品有無限多個,可以被拿無限多次
(2)狀態dp[j]表示到體積j為止的最大價值狀態轉移方程:
dp[j]=max(dp[j],dp[j-w]+v)跟一維01背包類似,但是不同點在于完全背包要從前往后遍歷,這樣每個物品能被拿無限多次,用新數據來更新新數據,而一維01背包要從后往前遍歷,確保每個物品只拿一次,用舊數據來更新新數據

小明的背包2

學習:
(1)模版題
代碼:

#include <bits/stdc++.h>using namespace std;
const int V=1e3+10;
int n,vol,dp[V];int main(){ios::sync_with_stdio(false);cin>>n>>vol;for(int i=1;i<=n;i++){int w,v;cin>>w>>v;for(int j=w;j<=vol;j++){ //正序遍歷 dp[j]=max(dp[j],dp[j-w]+v);}}cout<<dp[vol];return 0;
}

多重背包

(1)特征:第i個物品有 s i s_i si?個,共有s+1種狀態(取0,1,2…s個)
(2)核心解決辦法: s i s_i si?個第i個物品當成獨立的s個物品(遍歷s次即可),每個 s i j s_ij si?j物品就只有一個,就是01背包問題

小明的背包3

學習:
(1)多重背包模版題
代碼:

#include <bits/stdc++.h>using namespace std;
const int V=2e2+10;
int n,vol,dp[V];int main(){ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>vol;for(int i=1;i<=n;i++){int w,v,s;cin>>w>>v>>s;//遍歷s次,相當于把第i個物品拆成s個i_s物品,每個i_s物品只有一個,為01背包問題for(int k=1;k<=s;k++){//01背包倒序遍歷 for(int j=vol;j>=w;j--){dp[j]=max(dp[j],dp[j-w]+v);}} }cout<<dp[vol];return 0;
}
二進制優化多重背包

(1)將s個物品拆分成s組,每組一個的經典多重背包時間復雜度為O(n*s*V),s過大會超時
(2)因為任意一個數都有其對應的二進制數,令s=1+2+4+8+…+其他,一個物品可以分為約log2(s)組,就可以將幾個二進制數組合表示0-s這s+1種中的任意狀態,例如:
s=14=1+2+4+7(其他),要取10個物品,就相當于依次取1,2,7個物品即可,
所以可以將物品數量拆分為多個二進制組合,減少狀態轉移次數
(3)修改s的遍歷即可,不再是1個1個加,而是倍數乘,而2個物品對應修改為2w,2v即可,最終的復雜度為O(n*log2(s)*V)

新一的寶藏搜尋加強版

學習:
(1) s i s_i si?最大能到2e4,普通多重背包會超時,需要二進制優化,記得最后一個剩余的數要單獨遍歷一次
代碼:

#include <bits/stdc++.h>using namespace std;
const int V=2e4+10;
int n,vol,dp[V];int main(){ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>vol;for(int i=1;i<=n;i++){int w,v,s;cin>>w>>v>>s;int t=0;//t為二進制數累加和 for(int k=1;(k+t)<=s;k*=2){t+=k;//01背包倒序for(int j=vol;j>=k*w;j--){dp[j]=max(dp[j],dp[j-k*w]+k*v);}}//最后剩余部分為s-t for(int j=vol;j>=(s-t)*w;j--){dp[j]=max(dp[j],dp[j-(s-t)*w]+(s-t)*v);}}cout<<dp[vol];return 0;
}

二維費用背包

(1)特征:物品除了價值v,體積w兩個特征外,還多了一個重量m特征,現在有兩個限制條件:體積不超過W和重量不超過M的最大價值
(2)解決方法:一維01背包變成二維即可,仍然倒序更新,dp[i][j]表示體積到i為止,重量到j為止的最大價值,狀態轉移方程:dp[i][j]=max(dp[i][j],dp[i-w][j-m]+v)

小藍的神秘行囊

學習:
(1)二維費用背包模版題
代碼:

#include <bits/stdc++.h>using namespace std;
const int V=105,M=105;
int n,vol,mm,dp[V][M];int main(){ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>vol>>mm;for(int i=1;i<=n;i++){int v,m,w;cin>>v>>m>>w;//二維倒序遍歷for(int j=vol;j>=v;j--){for(int k=mm;k>=m;k--){dp[j][k]=max(dp[j][k],dp[j-v][k-m]+w);}}}cout<<dp[vol][mm];return 0;
}

分組背包

(1)特征:共有n組物品,每組物品里面有s個物品,每個物品有對應的w和v,每組物品最多只能取一個(區別所在),所有狀態轉移的第一維度是組,且沒用一維優化,因為每一組的w和v有多個,不是固定的
(2)解決問題:dp[i][j]表示到第i組為止,體積j為止的最大價值,狀態轉移方程(好好理解)

dp[i][j]=dp[i-1][j] //初始化這一組都不取,就是上一組的狀態(01背包是通過一個狀態轉移方程全做了)
dp[i][j]=max(dp[i][j],dp[i-1][j-w]+v) //注意第一個是dp[i][j],而不是dp[i-1][j],因為每一組最多取一個物品,相當于比較取第i組取之前某個物品的最大價值(dp[i][j])和第i組取當前這個物品的最大價值(dp[i-1][j-w]+v)相比較

(3)正序倒序無所謂,因為兩個維度了

小明的背包5

代碼:

#include <bits/stdc++.h>using namespace std;
const int N=105,V=105;
int n,vol,dp[N][V];int main(){ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n>>vol;//遍歷每一組for(int i=1;i<=n;i++){int s;cin>>s;//這一組商品都不拿,等價于初始化dp[i][j] for(int j=0;j<=vol;j++)	dp[i][j]=dp[i-1][j]; //遍歷每一組的每一個物品,考慮拿不拿 while(s--){int w,v;cin>>w>>v;//從上一組轉移過來,而不是從上一個物品轉移過來 for(int j=w;j<=vol;j++){dp[i][j]=max(dp[i][j],dp[i-1][j-w]+v); //第一個是dp[i][j],保證每一組最多取一個物品 }} } cout<<dp[n][vol];return 0;
}

藍橋杯真題

砝碼稱重

學習:
(1)不是典型的背包問題,但是將動態規劃的思想運用的淋漓盡致
首先定義一個狀態:dp[i][j]表示到第i個砝碼為止,到重量j為止,是否可以稱出重量j,為0/1,
再考慮i和j的邊界,i最多到n,而j最多到sum,不是到V
再考慮它由哪幾種狀態轉移而來:
1.不拿砝碼,由dp[i-1][j]轉移而來
2.拿砝碼放左側,由dp[i-1][abs(j-w)](放左側相當于加上w,由于鏡像,abs(j-w)->j)
3.拿砝碼放右側,由dp[i-1][j+w]轉移而來(放右側相當于減去w,j+w->j)
![[砝碼稱重.png]]
代碼:
(1)法一:好理解

#include <bits/stdc++.h>using namespace std;
typedef long long ll;
const int N=105,V=1e5+10;
int n,w[N],dp[N][V];//dp[i][j]表示到第i個砝碼為止,到重量j為止,是否可以稱出來,為0/1 int main(){ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n;ll sum=0;for(int i=1;i<=n;i++){cin>>w[i];sum+=w[i];}for(int i=1;i<=n;i++){for(int j=1;j<=sum;j++){ //最大重量到sum //1.好理解的//先從前i-1個砝碼狀態轉移過來dp[i][j]=dp[i-1][j];//如果上一個狀態稱不出來重量j,看看有了第i個砝碼能不能稱出重量jif(dp[i][j]==0){//當前砝碼就是重量jif(w[i]==j)	dp[i][j]=1;//重量j+w[i]能稱出來,當前砝碼放另一側,減去w[i],j+w[i]->j if(dp[i-1][j+w[i]])	dp[i][j]=1;//重量abs(j-w[i])能稱出來,當前砝碼放同側,加上w,abs(j-w[i])->j //取abs是因為不確定對于自己這一側來看重量是正是負(自己這一側為正值)if(dp[i-1][abs(j-w[i])])	dp[i][j]=1; } }} ll ans=0;for(int j=1;j<=sum;j++){if(dp[n][j]==1)	ans++;}cout<<ans;return 0;
}

(2)法二:

#include <bits/stdc++.h>using namespace std;
typedef long long ll;
const int N=105,V=1e5+10;
int n,w[N],dp[N][V];//dp[i][j]表示到第i個砝碼為止,到重量j為止,是否可以稱出來,為0/1 int main(){ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>n;ll sum=0;for(int i=1;i<=n;i++){cin>>w[i];sum+=w[i];}dp[0][0]=1; //保證當前砝碼就是重量j的情況for(int i=1;i<=n;i++){for(int j=0;j<=sum;j++){ //從0開始,最大重量到sum //2.三種狀態轉移而來,且dp[i][j]值為0或1,用或操作dp[i][j]=dp[i-1][j]||dp[i-1][j+w[i]]||dp[i-1][abs(j-w[i])];}} ll ans=0;for(int j=1;j<=sum;j++){ //從1開始if(dp[n][j]==1)	ans++;}cout<<ans;return 0;
}

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

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

相關文章

MyBatis-Plus 自動填充:優雅實現創建/更新時間自動更新!

目錄 一、什么是 MyBatis-Plus 自動填充&#xff1f; &#x1f914;二、自動填充的原理 ??三、實際例子&#xff1a;創建時間和更新時間字段自動填充 ?四、注意事項 ??五、總結 &#x1f389; &#x1f31f;我的其他文章也講解的比較有趣&#x1f601;&#xff0c;如果喜歡…

arduino R4 SD卡讀寫測試

使用買來的 st7789LCD 顯示器背面就帶著一個 tf 卡槽&#xff0c;可以直接連接 tf 卡。使用 Sdfat 庫就可以實現對 sd 卡的讀寫操作。這里嘗試測試 sd 卡的讀寫功能。 LCD 顯示器的初始化 //定義LCD的對象 Adafruit_ST7789 tft Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);tf…

【武漢·4月11日】Parasoft聯合光庭信息研討會|邀您共探AI賦能新機遇

Parasoft聯合光庭信息Workshop邀您共探AI賦能新機遇 AI浪潮已至&#xff0c;你準備好了嗎&#xff1f; 在智能網聯汽車飛速發展的今天&#xff0c;AI技術正以前所未有的速度重塑行業生態。如何把握AI機遇&#xff0c;賦能企業創新&#xff1f; 4月11日&#xff0c;自動化軟件…

VLLM專題(三十九)—自動前綴緩存(二)

前綴緩存(Prefix Caching)是一種在LLM推理中廣泛使用的優化技術,旨在避免冗余的提示詞(prompt)計算。其核心思想很簡單——我們緩存已處理請求的鍵值緩存(kv-cache)塊,并在新請求的前綴與之前請求相同時重用這些塊。由于前綴緩存幾乎是一種“免費的午餐”,并且不會改變…

自動駕駛系統的車輛動力學建模:自行車模型與汽車模型的對比分析

在自動駕駛系統的車輛動力學建模中&#xff0c;自行車模型&#xff08;Bicycle Model&#xff09;和更復雜的汽車模型&#xff08;如雙軌模型或多體動力學模型&#xff09;各有其適用場景和優缺點。以下是兩者的詳細對比及選擇原因解析&#xff1a; 1. 模型定義與核心差異 特性…

C語言入門教程100講(6)類型修飾符

文章目錄 1. 什么是類型修飾符&#xff1f;2. 常見的類型修飾符3. 類型修飾符的使用3.1 short 和 long3.2 signed 和 unsigned 4. 類型修飾符的組合5. 示例代碼代碼解析&#xff1a;輸出結果&#xff1a; 6. 常見問題問題 1&#xff1a;short 和 long 的具體大小是多少&#xf…

Linux-Ubuntu 系統學習筆記 | 從入門到實戰

&#x1f4d8; Linux-Ubuntu 系統學習筆記 | 從入門到實戰 &#x1f4dc; 目錄 環境安裝基本操作Linux操作系統介紹文件系統常用命令用戶權限管理編輯器vimGCC編譯器動態庫與靜態庫Makefile 1. 環境安裝 &#x1f31f; 下載鏡像 推薦使用清華大學開源鏡像站下載Ubuntu鏡像&a…

防火墻帶寬管理

拓撲 配置 [fw]interface GigabitEthernet 0/0/0 [fw-GigabitEthernet0/0/0]service-manage all permit [fw]interface GigabitEthernet 1/0/0 [fw-GigabitEthernet1/0/0]ip address 12.0.0.1 24 [fw]interface GigabitEthernet 1/0/1 [fw-GigabitEthernet1/0/1]ip ad…

一人系統 之 為什么要做一人系統?

一人系統 之 賺錢認知篇&#xff08;下&#xff09; 本文 2119個字&#xff0c;大概閱讀時間 16分鐘。 在上一篇文章中&#xff0c;主要講了以下三個內容&#xff1a; 什么是好的工作&#xff1f;時薪高&#xff0c;并且有能力提升&#xff0c;而且最終可以獨立創業的工作&…

基于springboot的電影院管理系統(源碼+lw+部署文檔+講解),源碼可白嫖!

摘要 互聯網技術的成熟和普及&#xff0c;勢必會給人們的生活方式帶來不同程度的改變。越來越多的經營模式中都少不了線上運營&#xff0c;互聯網正強力推動著社會和經濟發展。國人對民族文化的自信和不同文化的包容&#xff0c;再加上電影行業的發展&#xff0c;如此繁榮吸引…

Java安全-類的動態加載

類的加載過程 先在方法區找class信息&#xff0c;有的話直接調用&#xff0c;沒有的話則使用類加載器加載到方法區(靜態成員放在靜態區&#xff0c;非靜態成功放在非靜態區)&#xff0c;靜態代碼塊在類加載時自動執行代碼&#xff0c;非靜態的不執行;先父類后子類&#xff0c;…

ROS多機通信功能包——Multibotnet

引言 這是之前看到一位大佬做的集群通信中間件&#xff0c;突發奇想&#xff0c;自己也來做一個&#xff0c;實現更多的功能、更清楚的架構和性能更加高效的ROS多機通信的功能包 鏈接&#xff1a;https://blog.csdn.net/benchuspx/article/details/128576723 Multibotnet Mu…

C++:背包問題習題

1. 貨幣系統 1371. 貨幣系統 - AcWing題庫 給定 V 種貨幣&#xff08;單位&#xff1a;元&#xff09;&#xff0c;每種貨幣使用的次數不限。 不同種類的貨幣&#xff0c;面值可能是相同的。 現在&#xff0c;要你用這 V 種貨幣湊出 N 元錢&#xff0c;請問共有多少種不同的…

IT工具 | node.js 進程管理工具 PM2 大升級!支持 Bun.js

P(rocess)M(anager)2 是一個 node.js 下的進程管理器&#xff0c;內置負載均衡&#xff0c;支持應用自動重啟&#xff0c;常用于生產環境運行 node.js 應用&#xff0c;非常好用&#x1f44d; &#x1f33c;概述 2025-03-15日&#xff0c;PM2發布最新版本v6.0.5&#xff0c;這…

2025年01月02日浙江鼎永前端面試

目錄 webpack 和 vite 區別react fiber 架構vue diff 算法react diff 算法hooks 源碼垂直水平布局項目介紹單點登錄大文件上傳微前端 1. webpack 和 vite 區別 Webpack 和 Vite 是兩種不同的前端構建工具&#xff0c;它們在設計理念、性能表現和使用場景上存在顯著差異。以下…

1.企業級AD活動目錄核心解析:架構、組件與集成實踐

在當今數字化時代&#xff0c;企業級網絡環境日益復雜&#xff0c;高效、安全的資源管理和用戶認證成為企業 IT 運營的關鍵。AD&#xff08;Active Directory&#xff09;活動目錄作為微軟 Windows 系列服務器中的重要目錄服務&#xff0c;為企業級網絡管理提供了強大的解決方案…

【數據分享】2014-2024年我國各城市逐年空氣質量指數(AQI)數據

空氣質量指數&#xff08;AQI&#xff09;是一個衡量空氣污染程度的綜合指標&#xff0c;它并不直接表示具體污染物的濃度值&#xff0c;而是基于多種污染物的濃度進行的綜合評價&#xff0c;具體基于六種主要污染物的濃度&#xff1a;PM2.5、PM10、SO?、NO?、O?和CO。AQI是…

【C++】深入理解list迭代器的設計與實現

深入理解list迭代器的設計與實現 引言1、鏈表基礎結構2、鏈表迭代器的封裝2.1 初步封裝迭代器類2.2 引入const迭代器2.2.1 參考STL源代碼2.2.2 完善迭代器 3、迭代器實現機制結語 引言 在STL容器中&#xff0c;list作為經典的雙向鏈表容器&#xff0c;其迭代器設計體現了C模板編…

C語言基礎系列【27】typedef

博主介紹&#xff1a;程序喵大人 35- 資深C/C/Rust/Android/iOS客戶端開發10年大廠工作經驗嵌入式/人工智能/自動駕駛/音視頻/游戲開發入門級選手《C20高級編程》《C23高級編程》等多本書籍著譯者更多原創精品文章&#xff0c;首發gzh&#xff0c;見文末&#x1f447;&#x1f…

【CXX-Qt】2.5 繼承

某些 Qt API 要求你從抽象基類中重寫某些方法&#xff0c;例如 QAbstractItemModel。 為了支持直接從 Rust 中創建這樣的子類&#xff0c;CXX-Qt 提供了多種輔助工具。 某些基類可能需要特殊的構造參數。這可以通過使用自定義構造函數來實現。 訪問基類方法 要在 Rust 中訪…