day 42:01背包問題;416. 分割等和子集

動態規劃:01背包問題

  • 01背包問題基礎
    • 1. 暴力解法
    • 2. 二維dp數組01背包
      • 1.確定dp數組以及下標的含義
      • 2.遞推公式
      • 3.dp數組如何初始化
      • 4.遍歷順序
      • 5.測試代碼
  • 01背包理論基礎(滾動數組):將二維dp轉換為一維dp
    • 1. dp數組以及下標名義
    • 2. 遞歸公式
    • 3. dp數組如何初始化
    • 4. 遍歷順序:從后往前遍歷
    • 5.一維dp01背包完整C++測試代碼
  • 416. 分割等和子集
    • 1. dp數組以及下標名義
    • 2. 遞歸公式
    • 3. dp數組如何初始化
    • 4. 遍歷順序:從后往前遍歷
    • 5. 代碼

01背包問題基礎

1. 暴力解法

在這里插入圖片描述
每個物品有兩種狀態,放與不放,可通過回溯暴力求解,復雜度為N(2的n次方)

2. 二維dp數組01背包

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

對于背包問題,有一種寫法, 是使用二維數組,即dp[i][j] 表示從==,下標為==[0-i]==的物品里任意取,放進容量為j的背包,價值總和最大是多少。
![在這里插入圖片描述](https://img-blog.csdnimg.cn/f1dd9f281aca4f8a8c06bee3aece4b64.png在這里插入圖片描述

2.遞推公式

  • 放物品 i : 不放物品 i 時的最大價值加上放物品 i 的價值 :dp[i - 1][j - weight[ i ]] + value[ i ];
  • 不放物品i :dp[i - 1][j]推出,當前背包容量為 j ,
  • 遞推公式:dp[i ][j] = max(dp[i - 1][j],dp[i - 1][j - weight[ i ]] + value[ i ])

3.dp數組如何初始化

初始化第一列,背包容量為0,則價值都為0
初始化第一行,物品為0,當背包為0 時放不了所有價值為0;當背包為1時剛好放下物品,所有價值為15;后序同理為15
非零下標:初始化值不影響
在這里插入圖片描述

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

4.遍歷順序

二維dp可以先遍歷物品也可先遍歷背包
為什么?
因為:遞歸公式中可以看出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]的左上角方向(包括正上方向),那么先遍歷物品,再遍歷背包的過程如圖所示:
雖然兩個for循環遍歷的次序不同,但是dp[i][j]所需要的數據就是左上角,根本不影響dp[i][j]公式的推導!

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

5.測試代碼

void test_2_wei_bag_problem1() {vector<int> weight = {1, 3, 4};vector<int> value = {15, 20, 30};int bagweight = 4;// 二維數組vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0));// 初始化for (int j = weight[0]; j <= bagweight; j++) {dp[0][j] = value[0];}// weight數組的大小 就是物品個數for(int i = 1; i < weight.size(); i++) { // 遍歷物品for(int j = 0; j <= bagweight; j++) { // 遍歷背包容量if (j < weight[i]) dp[i][j] = dp[i - 1][j];else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);}}cout << dp[weight.size() - 1][bagweight] << endl;
}int main() {test_2_wei_bag_problem1();
}

01背包理論基礎(滾動數組):將二維dp轉換為一維dp

1. dp數組以及下標名義

dp[j]:容量為j的背包所能裝的最大價值。

2. 遞歸公式

  • 不放物品i :dp[j]可以由dp[j - weight[i]]推出,當前背包容量為 j ,

  • 放物品 i : 不放物品 i 時的最大價值加上放物品 i 的價值 :dp[j - weight[ i ]] + value[ i ];

  • 遞推公式:max(dp[j], dp[j - weight[ i ]] + value[ i ])入代碼片

3. dp數組如何初始化

dp[0] = 0
非零下標:dp數組在推導過程中一定是取價值最大的數,如果題目給的價值都是正整數那么非零下標都初始化為0就可以了,這樣才能讓dp數組在遞推公式的過程中取得最大的價值,而不是被初始值覆蓋掉

4. 遍歷順序:從后往前遍歷

for(int i = 0; i < weight.size(); i++) {//遍歷物品for(int j = bageweight; j >= weight[i]; j--) {//遍歷背包容量dp[j] = max(dp[j], dp[j - weight[ i ]] + value[ i ]);}
}

倒序原因
舉例如下表,weight[0] = 1, 價值 value[0] = 15,如果正序遍歷
在這里插入圖片描述

正序
dp[1] = dp[1 - weight[0]] + value[0] = 15
dp[2] = dp[2 - weight[0]] + value[1] = 30  此時物品0 被放了兩次倒序,先計算dp[2]
dp[2] = dp[2 - weight[0]] + value[1] = 15  因為初始化了dp[1]0
dp[1] = dp[1 - weight[0]] + value[0] = 15

為什么倒序
倒序遍歷的原因是,本質上還是一個對二維數組的遍歷,并且右下角的值依賴上一層左上角的值,因此需要保證左邊的值仍然是上一層的,從右向左覆蓋。
為什么二維數組不用倒序
因為對于二維dp,dp[i][j]都是通過上一層即dp[i - 1][j]計算而來,本層的dp[i][j]并不會被覆蓋!
為什么不可以先遍歷背包再遍歷物品
因為一維dp的寫法,背包容量一定是要倒序遍歷,如果遍歷背包容量放在上一層,那么每個dp[j]就只會放入一個物品,即:背包里只放入了一個物品。

5.一維dp01背包完整C++測試代碼

void test_1_wei_bag_problem() {vector<int> weight = {1, 3, 4};vector<int> value = {15, 20, 30};int bagWeight = 4;// 初始化vector<int> dp(bagWeight + 1, 0);for(int i = 0; i < weight.size(); i++) { // 遍歷物品for(int j = bagWeight; j >= weight[i]; j--) { // 遍歷背包容量dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);}}cout << dp[bagWeight] << endl;
}int main() {test_1_wei_bag_problem();
}

416. 分割等和子集

首先,本題要求集合里能否出現總和為 sum / 2 的子集

1. dp數組以及下標名義

dp[j]:容量為j的背包所能裝的最大價值。

2. 遞歸公式

-本題相當于背包里放入數值,那么物品i的重量是nums[i],其價值也是nums[i]。

  • 遞推公式:max(dp[j], dp[j - nums[i]] + nums[i])入代碼片

3. dp數組如何初始化

dp[0] = 0
非零下標:dp數組在推導過程中一定是取價值最大的數,如果題目給的價值都是正整數那么非零下標都初始化為0就可以了,這樣才能讓dp數組在遞推公式的過程中取得最大的價值,而不是被初始值覆蓋掉

/ 總和不會大于20000,背包最大只需要其中一半,所以10001大小就可以了
vector<int> dp(10001, 0);

4. 遍歷順序:從后往前遍歷

5. 代碼

class Solution {
public:bool canPartition(vector<int>& nums) {vector<int>dp(10001 , 0);int sum = 0;for(int i = 0; i < nums.size(); i++) {sum +=nums[i];}if(sum % 2 == 1)return false;for (int i = 0; i < nums.size(); i++) {for(int j = sum/2; j >= nums[i]; j--) {dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);}}if(dp[sum/2] == sum / 2)return true;return false;}
};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處:https://dhexx.cn/hk/4627793.html

如若內容造成侵權/違法違規/事實不符,請聯系我的編程經驗分享網進行投訴反饋,一經查實,立即刪除!


相關文章:

  • day 38,509. 斐波那契數70. 爬樓梯;# 746. 使用最小花費爬樓梯
  • 論文筆記--PANGU-α
  • SSRF漏洞、SQL注入、CSRF漏洞、XXE漏洞
  • 總結8881
  • 聲音的變奏:深入理解ffmpeg音頻格式轉換的奧秘與應用
  • 第6節:obj/fbx/shp等轉3dtiles(免費轉換工具+視頻)
  • 皮卡丘Over Permission
  • Temporal.Duration 規范用法
  • 對Java遠程熱部署實踐學習和分析
  • 復習Linux——01 基礎命令
  • 【23種設計模式】觀察者模式(Observer Pattern)
  • 代碼隨想錄算法訓練營第四十六天|139.單詞拆分、關于多重背包,你該了解這些!、背包問題總結篇!
  • 【ORACLE】一條看不出會刪哪個表的delete語句
  • day 39:62. 不同路徑63. 不同路徑 II
  • Python hashlib 加密方法(MD5、SHA1、SHA256、SHA52)
  • MIME 類型-01
  • 舊改快訊--桑泰南山桃源“工改商住”項目規劃修改
  • 從零開始學習JVM(六)-直接內存和執行引擎
  • day 46: 背包總結
  • 背包問題總結篇
  • 【ros/ros2】ros2 humble鏡像制作過程中碰到的問題記錄
  • pikachu靶場總結
  • RVOS環境搭建-01
  • UE里面函數 自定義事件 宏的區別
  • redis緩存單體服務測試本地鎖失效問題
  • 【網絡原理】數據鏈路層 和 應用層 重點協議
  • SpringCloud(四)
  • Docker 常用命令操作
  • eBPF 入門開發實踐教程十一:在 eBPF 中使用 libbpf 開發用戶態程序并跟蹤 exec() 和 exit() 系統調用
  • Apache Kafka - 流式處理
  • Java同步容器類
  • ASP.NET Core MVC 從入門到精通之自動映射(二)
  • day 41:343. 整數拆分;96.不同的二叉搜索樹
  • Dubbo spi機制
  • 皮卡丘RCE
  • 【C++刷題】【動態規劃篇】(一)
  • mysql密碼字段類型
  • 什么是“支付二清”,“二清”的定義
  • 【6.01 代隨_44day】 完全背包、零錢兌換 II、組合總和 Ⅳ
  • day 44 完全背包:518. 零錢兌換 II;377. 組合總和 Ⅳ
  • 使用OpenAI創建對話式聊天機器人
  • 以太網交換機自學習和轉發幀的流程
  • 《商用密碼應用與安全性評估》第四章密碼應用安全性評估實施要點4.6測評報告編制報送和監督檢查
  • 二叉搜索樹桶排序
  • 文件上傳漏洞、XSS漏洞、RCE漏洞
  • 總投資300億,南山前海南山村舊改城市更新
  • 如何在 Python 中循環字典
  • 計組 第二章錯題 2.3 浮點數的表示與運算
  • 【MySQL高級篇筆記-性能分析工具的使用 (中) 】
  • 什么是CMS?企業開發使用什么CMS?