動態規劃-01背包

兜兜轉轉了半天,發現還是Carl寫的好。

看過動態規劃-基礎的讀者,大概都清楚。

動態規劃是將大問題,分解成子問題。并將子問題的解儲存下來,避免重復計算。

而背包問題,就是動態規劃延申出來的一個大類。

而01背包,就隸屬于背包問題。

那什么又是01背包呢?

01背包

有n件物品,與一次最多能背w重量的背包。第i件物品,重量為weight[i],得到的價值為value[i]。

每件物品只能用一次,求解,將那些物品裝入背包內,物品的價值總和最大。?

重量(weight)價值(value)
物品0115
物品1320
物品2430

這是一個標準的背包問題,很多一看到這個,就直接想起用動態規劃,而忽略了暴力解法。

這是因為沒有 自下而上 思考的結果。

如下代碼,一般動態規劃問題,都是能通過回溯解決,因為每個物品都有兩種可能(狀態),

被放入背包,或者不放入背包。

// 全局變量用于記錄最大價值
int maxValue = 0;// 物品的重量和價值數組
vector<int> weights = {1, 3, 4, 5, 6};
vector<int> values = {1, 3, 4, 5, 6};// 背包容量
int capacity = 10;// 回溯函數
void backtrack(int index, int currentWeight, int currentValue) {// 如果已經遍歷完所有物品if (index == weights.size()) {// 更新最大價值if (currentValue > maxValue) {maxValue = currentValue;}return;}// 不選擇當前物品 - 01背包中的0backtrack(index + 1, currentWeight, currentValue);// 選擇當前物品   - 01背包中的1if (currentWeight + weights[index] <= capacity) {backtrack(index + 1, currentWeight + weights[index], currentValue + values[index]);}
}

如上的回溯算法,每個問題都有兩個解法,通過暴力解決,但通常這種解法,是O(2^n)的時間復雜度,隨著數量的增加。

呈指數級上升。

而動態規劃僅僅需要O(N*M)就可以解決。

第一步:下標含義
dp[i][j]表示將前i件物品裝進限重為j的背包可以獲得的最大價值, 0<=i<=N, 0<=j<=W
第二步:推導公式

那么我們可以將dp[0][0...W]初始化為0,表示將前0個物品(即沒有物品),裝入書包的最大價值為0。那么當i>0時,dp[i][j]有兩種情況:

  1. 不裝入第i件物品,即dp[i?1][j]
  2. 裝入第i件物品(前提是能裝下),即dp[i-1][j-weight[i]]+value[i]。
第三步:書寫代碼
dp[weight.size()][bagweight + 1];// 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]);}
}
壓縮

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

看到 dp[i][j] 與 dp[i-1][...] 的

大家都可以發現,dp都由上一行推導出來的(也就是把dp[i - 1]那一層拷貝到dp[i]上),所以可以壓縮代碼。

把二維數組,壓縮為一維滾動數組

這也就是滾動數組的由來,需要滿足的條件是上一層可以重復利用,直接拷貝到當前層。

需要注意的是,為了防止上一層循環的dp[0,...,j-1]被覆蓋,循環的時候 j 只能逆向枚舉

如下:

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] );}
}

大綱?

1、分割等和子集

?2、最后一塊石頭的重量 II

?3、目標和

?4、一和零

題目

1、分割等和子集

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

示例 1:

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

示例 2:

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

提示:

  • 1 <= nums.length <= 200
  • 1 <= nums[i] <= 100
class Solution {// 最大也就意味著最接近// 能通過動態規劃解決的,都能通過回溯解決// 每個數字都有兩種狀態,被選中,或者不被選中// 只有單純的數字,那么數字的大小是重量,也是價值。
public:bool canPartition(vector<int>& nums) {int cur = 0;for(int i:nums) cur+=i;int sum = cur/2;if(sum*2 != cur) return false; // 意外情況,直接排除vector<int> dp(sum+1,0);for(int i=0; i<nums.size(); i++){for(int j=sum; j>=nums[i]; --j){dp[j] = max(dp[j], dp[j-nums[i]]+nums[i]);}}return sum==dp[sum]?true:false;}
};

2、最后一塊石頭的重量 II

有一堆石頭,用整數數組?stones?表示。其中?stones[i]?表示第?i?塊石頭的重量。

每一回合,從中選出任意兩塊石頭,然后將它們一起粉碎。假設石頭的重量分別為?x?和?y,且?x <= y。那么粉碎的可能結果如下:

  • 如果?x == y,那么兩塊石頭都會被完全粉碎;
  • 如果?x != y,那么重量為?x?的石頭將會完全粉碎,而重量為?y?的石頭新重量為?y-x

最后,最多只會剩下一塊?石頭。返回此石頭?最小的可能重量?。如果沒有石頭剩下,就返回?0

示例 1:

輸入:stones = [2,7,4,1,8,1]
輸出:1
解釋:
組合 2 和 4,得到 2,所以數組轉化為 [2,7,1,8,1],
組合 7 和 8,得到 1,所以數組轉化為 [2,1,1,1],
組合 2 和 1,得到 1,所以數組轉化為 [1,1,1],
組合 1 和 1,得到 0,所以數組轉化為 [1],這就是最優值。

示例 2:

輸入:stones = [31,26,33,21,40]
輸出:5

提示:

  • 1 <= stones.length <= 30
  • 1 <= stones[i] <= 100

為啥要分兩堆->一直在動態的動態的維護,從第一塊進入開始,一直在動態的維護兩堆的平衡。

class Solution {// 對吶,只要讓兩撥石頭血拼就行!// 但是,為啥要讓兩撥石頭血拼? // 圖片上附上解析,希望以后能看懂
public:int lastStoneWeightII(vector<int>& stones) { int sum = 0;for(int i : stones) sum+=i;int cur = sum;sum>>=1; // 右移2位,相當于除以2;vector<int> dp(sum+1, 0);for(int i=0; i<stones.size(); ++i){for(int j = sum; j>=stones[i]; --j){dp[j] = max(dp[j],dp[j-stones[i]]+stones[i]);}}return cur-2*dp[sum];}
};

3、目標和

給你一個非負整數數組?nums?和一個整數?target?。

向數組中的每個整數前添加?'+'?或?'-'?,然后串聯起所有整數,可以構造一個?表達式?:

  • 例如,nums = [2, 1]?,可以在?2?之前添加?'+'?,在?1?之前添加?'-'?,然后串聯起來得到表達式?"+2-1"?。

返回可以通過上述方法構造的、運算結果等于?target?的不同?表達式?的數目。

示例 1:

輸入:nums = [1,1,1,1,1], target = 3
輸出:5
解釋:一共有 5 種方法讓最終目標和為 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3

示例 2:

輸入:nums = [1], target = 1
輸出:1

提示:

  • 1 <= nums.length <= 20
  • 0 <= nums[i] <= 1000
  • 0 <= sum(nums[i]) <= 1000
  • -1000 <= target <= 1000

class Solution {// 如果用暴力解法,本題也是能做的// 但是如果我不暴力呢?// 了解過答案之后,就發現這道題目,純純是一道推理題。// 用方法就行推理,真tm是一道推理題// (cur+)+(cur-) = target;// (cur+)-(cur-) = target;//  cur = (sum-target)/2; 由此公式推導// 只要找到cur就OK了// 當sum為0時,代表總和與target相同,都只有一種情況
public:int findTargetSumWays(vector<int>& nums, int target) {int cur = 0;for(int i:nums) cur+=i;int sum = (cur-target)>>1;if(sum*2!=cur-target||sum<0) return 0; // 直接就沒有可能了vector<int> dp(sum+1);dp[0]=1;// 公式推導出來的正整數for(int i=0; i<nums.size(); ++i){for(int j=sum; j>=nums[i]; --j){dp[j]=dp[j-nums[i]]+dp[j];}}return dp[sum];}
}; // ???我的腦袋里,有個大大的問題?這能過??

4、一和零

給你一個二進制字符串數組?strs?和兩個整數?m?和?n?。

請你找出并返回?strs?的最大子集的長度,該子集中?最多?有?m?個?0?和?n?個?1?。

如果?x?的所有元素也是?y?的元素,集合?x?是集合?y?的?子集?。

示例 1:

輸入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
輸出:4
解釋:最多有 5 個 0 和 3 個 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他滿足題意但較小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不滿足題意,因為它含 4 個 1 ,大于 n 的值 3 。

示例 2:

輸入:strs = ["10", "0", "1"], m = 1, n = 1
輸出:2
解釋:最大的子集是 {"0", "1"} ,所以答案是 2 。

提示:

  • 1 <= strs.length <= 600
  • 1 <= strs[i].length <= 100
  • strs[i]?僅由?'0'?和?'1'?組成
  • 1 <= m, n <= 100
class Solution {// 直接就干到n的三次方了!我的天吶,太牛了public:int findMaxForm(vector<string>& strs, int m, int n) {vector<vector<int>> dp(m+1,vector<int>(n+1,0)); for(string str : strs){int num0=0,num1=0;for(char c : str){if(c=='0') num0++;else num1++;}for(int i=m; i>=num0; --i){for(int j=n; j>=num1; --j){dp[i][j] = max(dp[i][j],dp[i-num0][j-num1]+1);}}}return dp[m][n];}
};

完結( ̄︶ ̄)↗ ,自己是收益匪淺啦


博客借鑒:

1、動態規劃之背包問題系列

2、動態規劃:01背包理論基礎


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

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

相關文章

使用VS2022編譯CEF

前提 選擇編譯的版本 CEF自動編譯&#xff0c;在這里可以看到最新的穩定版和Beta版。 從這里得出&#xff0c;最新的穩定版是134.0.6998.118&#xff0c;對應的cef branch是6998。通過這個信息可以在Build requirements查到相關的軟件配置信息。 這里主要看Windows下的編譯要…

C++20:玩轉 string 的 starts_with 和 ends_with

文章目錄 一、背景與動機二、string::starts_with 和 string::ends_with&#xff08;一&#xff09;語法與功能&#xff08;二&#xff09;使用示例1\. 判斷字符串開頭2\. 判斷字符串結尾 &#xff08;三&#xff09;優勢 三、string_view::starts_with 和 string_view::ends_w…

智能飛鳥監測 守護高壓線安全

飛鳥檢測新紀元&#xff1a;視覺分析技術的革新應用 在現代化社會中&#xff0c;飛鳥檢測成為了多個領域不可忽視的重要環節。無論是高壓線下的安全監測、工廠內的生產秩序維護&#xff0c;還是農業區的作物保護&#xff0c;飛鳥檢測都扮演著至關重要的角色。傳統的人工檢測方…

ADC噪聲全面分析 -04- 有效噪聲帶寬簡介

為什么要了解ENBW&#xff1f; 了解模數轉換器 (ADC) 噪聲可能具有挑戰性&#xff0c;即使對于最有經驗的模擬設計人員也是如此。 Delta-sigma ADC 具有量化和熱噪聲的組合&#xff0c;這取決于 ADC 的分辨率、參考電壓和輸出數據速率 (ODR)。 在系統級別&#xff0c;額外的信…

STM32單片機uCOS-Ⅲ系統10 內存管理

目錄 一、內存管理的基本概念 二、內存管理的運作機制 三、內存管理的應用場景 四、內存管理函數接口講解 1、內存池創建函數 OSMemCreate() 2、內存申請函數 OSMemGet() 3、內存釋放函數 OSMemPut() 五、實現 一、內存管理的基本概念 在計算系統中&#xff0c;變量、中…

考研課程安排(自用)

文章目錄 408數據結構&#xff08;王道&#xff09;計算機組成原理&#xff08;王道&#xff09;操作系統&#xff08;王道&#xff09;計算機網絡&#xff08;湖科大版&#xff09; 數學一高等數學&#xff08;微積分&#xff09;線性代數和概率論 408 數據結構&#xff08;王…

ultraiso制作u盤啟動

UltraISO制作U盤啟動盤的方法 UltraISO是一款功能強大的工具&#xff0c;可以幫助用戶將ISO鏡像文件寫入U盤&#xff0c;從而制作成可啟動的系統安裝盤。以下是詳細的步驟和注意事項&#xff1a; 1. ?準備工作? ?硬件準備?&#xff1a;一個容量至少為8GB的U盤&#xff0…

C語言-發布訂閱模式詳解與實踐

文章目錄 C語言發布訂閱模式詳解與實踐1. 什么是發布訂閱模式&#xff1f;2. 為什么需要發布訂閱模式&#xff1f;3. 實際應用場景4. 代碼實現4.1 UML 關系圖4.2 頭文件 (pubsub.h)4.3 實現文件 (pubsub.c)4.4 使用示例 (main.c) 5. 代碼分析5.1 關鍵設計點5.2 實現特點 6. 編譯…

藍橋杯2023年第十四屆省賽真題-異或和之差

題目來自DOTCPP&#xff1a; 思路&#xff1a; 什么是異或和&#xff1f; ①題目要求我們選擇兩個不相交的子段&#xff0c;我們可以枚舉一個分界線i&#xff0c;子段1在 i 的左邊&#xff0c; 子段2在 i 的右邊&#xff0c;分別找到子段1和子段2的最大值、最小值。 ②怎么確…

Linux作業2——有關文件系統權限的練習

1、創建/www目錄&#xff0c;在/www目錄下新建name和https目錄&#xff0c;在name和https目錄下分別創建一個index.html文件&#xff0c;name下面的index.html文件中包含當前主機的主機名&#xff0c;https目錄下的index.html文件中包含當前主機的ip地址。 #創建/www目錄&…

leeCode 70. 爬樓梯

假設你正在爬樓梯。需要 n 階你才能到達樓頂。 每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢&#xff1f; 示例 1&#xff1a; 輸入&#xff1a;n 2 輸出&#xff1a;2 解釋&#xff1a;有兩種方法可以爬到樓頂。 1. 1 階 1 階 2. 2 階 示例 2&#x…

算法題(105):小貓爬山

審題&#xff1a; 本題需要我們找出將n個小貓放在有限重的纜車上運下山所需的最小纜車數 時間復雜度分析&#xff1a;本題的數據量小于等于18&#xff0c;所以我們在做好剪枝的前提下可以使用深度優先搜索解題 思路&#xff1a; 方法一&#xff1a;dfs 搜索策略&#xff1a;將小…

第十六章:Specialization and Overloading_《C++ Templates》notes

Specialization and Overloading 一、模板特化與重載的核心概念二、代碼實戰與測試用例三、關鍵知識點總結四、進階技巧五、實踐建議多選題設計題代碼測試說明 一、模板特化與重載的核心概念 函數模板重載 (Function Template Overloading) // 基礎模板 template<typename…

多協議兼容+高并發處理:EasyCVR如何破解AI安防規模化落地難題?

隨著AI技術在安防領域的深入應用&#xff0c;規模化部署面臨兩大核心挑戰&#xff1a;設備協議碎片化導致的接入壁壘與海量視頻流并發帶來的性能瓶頸。TSINGSEE青犀視頻的EasyCVR平臺通過“多協議兼容高并發處理”雙引擎驅動&#xff0c;結合云邊端協同架構與智能算法優化&…

IntelliJ IDEA 中 Git 高頻問題與操作詳解|新手避坑指南

標簽&#xff1a;IntelliJ IDEA Git操作, Git教程, 版本控制, 沖突解決, 分支管理 引言 你是否遇到過這些問題&#xff1f; 代碼提交后想撤銷怎么辦&#xff1f;合并分支時沖突不會解決&#xff1f;不小心把錯誤代碼推送到遠程倉庫&#xff1f; 本文針對 IntelliJ IDEA 中 Git …

【聊聊層次式架構設計:像搭樂高一樣構建軟件大廈】

文章目錄 聊聊層次式架構設計&#xff1a;像搭樂高一樣構建軟件大廈理論篇&#xff1a;層次式架構的“千層套路”最底層&#xff1a;基礎設施層——默默付出的“基石俠”數據訪問層&#xff1a;“數據快遞員”業務邏輯層&#xff1a;智慧的“大腦中樞”表示層&#xff1a;軟件的…

N列股票收盤價為起點的馬科維茨(Markowitz)均值—方差理論

1. 數據準備與收益率計算 輸入數據&#xff1a; 假設你有一個矩陣&#xff0c;每一列代表一只股票的歷史收盤價序列。每一行對應一個時間點的收盤價。 計算收益率&#xff1a; 馬科維茨理論要求使用資產的收益率而非價格。常用的收益率計算方法有對數收益率或簡單收益率。 2.…

Conda常用命令匯總(持續更新中)

原文章&#xff1a;安裝和使用Miniconda來管理Python環境-CSDN博客 一、Miniconda的使用 Miniconda沒有GUI界面&#xff0c;只能通過conda命令對Python環境和軟件包進行管理&#xff0c;所以這里主要介紹一下conda的常用命令。 1. Conda相關 (1)查詢conda版本 conda --vers…

Redis Cluster 詳解

Redis Cluster 詳解 1. 為什么需要 Redis Cluster&#xff1f; Redis 作為一個高性能的內存數據庫&#xff0c;在單機模式下可能會遇到以下問題&#xff1a; 單機容量受限&#xff1a;Redis 是基于內存存儲的&#xff0c;單機的內存資源有限&#xff0c;單實例的 Redis 只能…

利用 MATLAB/Simulink 建立完整的控制系統模型,并進行階躍響應和負載擾動響應仿真

-利用 MATLAB/Simulink 建立完整的控制系統模型,包括單一控制回路(電流、速度、位置)和整個系統的級聯模型 仿真任務包括驗證各回路的階躍響應、負載擾動響應等,確保系統在動態性能上滿足設計要求。 以下是在MATLAB/Simulink中建立完整控制系統模型(包含單一控制回路和級聯…