背包初步(0-1背包、完全背包)

當月光灑在我的臉上
我想我就快變了模樣
有一種叫做撕心裂肺的湯
喝了它有神奇的力量

動態規劃初步(完全背包)

目錄

  • 動態規劃初步(完全背包)
    • 0-1背包簡介
    • 完全背包
      • 檢查數組是否存在有效劃分(前綴劃分DP)
      • 單詞拆分(求排列數)
      • 瘋狂的采藥(求組合數)
      • 自然數拆分

0-1背包簡介

在「背包 dp」前,先來看如下的例題:

P2871 [USACO07DEC] Charm Bracelet S - 洛谷

有n個物品和一個容量為m的背包,每個物品有重量 w和價值 v兩種屬性,要求選若干物品放入背包使背包中物品的總價值最大且背包中物品的總重量不超過背包的容量。

在上述例題中,由于每個物體只有兩種可能的狀態(取與不取),對應二進制中的0和1,這類問題便被稱為「0-1 背包問題」。

dp狀態f(i,j)為在只能放前i個物品的情況下,容量為j的背包所能達到的最大總價值。

考慮轉移。假設當前已經處理好了前i-1個武平的所有狀態,那么對于第i個物品:

  • 當其不放入背包時,背包的剩余容量不變,背包中物品的總價值也不變,故這種情況的最大價值為f(i-1,j);
  • 當其放入背包時,背包的剩余容量會減小v,背包中物品的總價值會增大w,故這種情況的最大價值為f(i-1,j-w)+v。

由此可以得出狀態轉移方程:fi,j=max?(fi?1,j,fi?1,j?wi+vi)f_{i,j}=\max(f_{i-1,j},f_{i-1,j-w_i}+v_i)fi,j?=max(fi?1,j?,fi?1,j?wi??+vi?)

但是,這里如果直接采用二維數組對狀態進行記錄,會出現MLE。可以考慮改用滾動數組的形式來優化。

由于對f(i)有影響的只有f(i-1),可以去掉第一維,直接用f(i)來表示處理到當前物品時背包容量為i的最大價值,得出以下方程:fj=max?(fj,fj?wi+vi)f_j=\max\left(f_j,f_{j-w_i}+v_i\right)fj?=max(fj?,fj?wi??+vi?)

大部分背包問題的轉移方程都是在此基礎上推導出來的。

核心代碼就這個狀態轉移

for(int j=m;j>0;j--){if(w<=j)dp[j]=max(dp[j],dp[j-w]+d);
}

相關練習

P1048 [NOIP 2005 普及組] 采藥 - 洛谷

P1049 [NOIP 2001 普及組] 裝箱問題 - 洛谷

P1060 [NOIP 2006 普及組] 開心的金明 - 洛谷

278. 數字組合 - AcWing題庫

P1164 小A點菜 - 洛谷


完全背包

完全背包模型與0-1背包類似,與0-1背包的區別僅在于一個物品可以選取無限次,而非僅能選取一次。

我們可以借鑒0-1背包的思路,進行狀態定義:設f()i,j為只能選前i個物品時,容量為j的背包可以達到的最大價值。

雖然定義與 0-1 背包類似,但是其狀態轉移方程與0-1背包并不相同。

可以考慮一個樸素的做法:對于第i件物品,枚舉其選了多少個來轉移。這樣做的時間復雜度是O(n3)的。

狀態轉移方程如下:

fi,j=max?k=0+∞(fi?1,j?k×wi+vi×k)f_{i,j}=\max_{k=0}^{+\infty}(f_{i-1,j-k\times w_i}+v_i\times k)fi,j?=maxk=0+?(fi?1,j?k×wi??+vi?×k)

做一個簡單的優化。可以發現,對于f(i,j),只要通過f(i,j-w)轉移就可以了。因此狀態轉移方程為:

fi,j=max?(fi?1,j,fi,j?wi+vi)f_{i,j}=\max(f_{i-1,j},f_{i,j-w_i}+v_i)fi,j?=max(fi?1,j?,fi,j?wi??+vi?)

與 0-1 背包相同,我們可以將第一維去掉來優化空間復雜度

fj=max?(fj,fj?wi+vi)f_{j}=\max(f_j,f_{j-w_i}+v_i)fj?=max(fj?,fj?wi??+vi?)

完全背包在寫的時候根據題目目的的不同還要討論兩層for循環的前后順序

  • 如果求組合數 就是外層for循環遍歷物品,內層for遍歷背包
  • 如果求排列數 就是外層for遍歷背包,內層for循環遍歷物品

完全背包和 0-1 背包的區別就在于對時間大小枚舉的順序不同,0-1背包在內層循環時是倒序的,因為每個物品只能選一次,倒序選擇保證了第i個物品只會被放入背包一次;倒序循環滿足了0-1背包問題中物品唯一的要求;而完全背包沒有數量的限制所以可以正序枚舉;


檢查數組是否存在有效劃分(前綴劃分DP)

2369. 檢查數組是否存在有效劃分 - 力扣(LeetCode)

為了下面一道題能理解完全背包的劃分思想,我已先看了這一道題;仔細觀察發現居然是麻將!!

我們要在手牌中判斷能否湊成對子,刻子和順子;但是不能排序,所以必須要是子區間才可以;

一開始想的是用前兩天學的滑動窗口,但是無法實現,就看題目的提示開始dp;直接套用分析的模板開始分析!

DP五部曲:

  1. 狀態定義:

    dp[i] 表示考慮數組的前 i 個元素(即 nums[0]nums[i-1])是否能被有效劃分(true/false)。

  2. 狀態轉移:

    通過三種子數組劃分方式進行狀態轉移:

    • 情況1:最后2個元素相等時,從 dp[i-2] 轉移
      dp[i] |= (nums[i-1] == nums[i-2]) && dp[i-2]

    • 情況2:最后3個元素相等時,從 dp[i-3] 轉移
      dp[i] |= (i>=3 && nums[i-1]==nums[i-2] && nums[i-2]==nums[i-3]) && dp[i-3]

    • 情況3:最后3個元素連續遞增(差值1)時,從 dp[i-3] 轉移
      dp[i] |= (i>=3 && nums[i-1]==nums[i-2]+1 && nums[i-2]==nums[i-3]+1) && dp[i-3]

    狀態轉移方程
    dp[i] = (最后2個相等且dp[i-2]) || (最后3個相等且dp[i-3]) || (最后3個連續遞增且dp[i-3])

  3. 初始化(邊界值):

    • dp[0] = true; // 空數組視為可劃分
    • dp[1] = false; // 單個元素不可劃分
  4. 遍歷順序:

    • 正序遍歷索引 i2n(含端點)
    • dp[i] 依賴 dp[i-2]dp[i-3],需優先計算小索引
  5. 返回形式:返回 dp[n],表示整個數組能否被有效劃分

分析完后,整體代碼就呼之欲出

class Solution {
public:bool validPartition(vector<int>& a) {if(a.size()<2)return 0;vector<bool> dp(a.size()+1,0);dp[0]=1;for(int i=2;i<=a.size();i++){bool f1=0,f2=0,f3=0;if(a[i-1]==a[i-2]){f1=dp[i-2];}if(i>2&&a[i-1]==a[i-2]&&a[i-1]==a[i-3]){f2=dp[i-3];}if(i>2&&a[i-1]==a[i-2]+1&&a[i-1]==a[i-3]+2){f3=dp[i-3];}dp[i]=f1||f2||f3;}return dp[a.size()];}
};

從這題開始,下面的才是正宗的完全背包

單詞拆分(求排列數)

139. 單詞拆分 - 力扣(LeetCode)

鋪墊那么久,終于開始引入正題;題目要求用給定的字符串數組中的元素拼出s,不限數量;不要求全部用上,但是拼的時候不能有重疊部分;我們需要判斷能否拼出來;里面單詞就是物品,字符串s就是背包,單詞能否組成字符串s,就是問物品能不能把背包裝滿。也就是我們所學的完全背包;

DP五部曲:

  1. 狀態定義:

    dp[i] : 字符串長度為i的話,dp[i]為true,表示可以拆分為一個或多個在字典中出現的單詞。

  2. 狀態轉移:確定遞推公式

    • 如果確定dp[j] 是true,且 [j, i] 這個區間的子串出現在字典里,那么dp[i]一定是true(j < i )。

    狀態轉移方程

    if ([j, i] 這個區間的子串出現在字典里 && dp[j]是true) dp[i] = true

  3. 初始化(邊界值):

    從遞推公式中可以看出,dp[i] 的狀態依靠 dp[j]是否為true,那么dp[0]就是遞推的根基,dp[0]一定為true,否則遞推下去后面都都是false了。

  4. 遍歷順序:

    題目中說是拆分為一個或多個在字典中出現的單詞,所以這是完全背包。還要討論兩層for循環的前后順序。這道題需要剛好拼滿,不能重疊不能遺漏;所以是求排列數,外層for遍歷背包,內層for循環遍歷物品。(其實就是為了方便截取字符串去比較)

  5. 返回形式: dp[s.size()]就是最終結果。

class Solution {
public:bool wordBreak(string s, vector<string>& a) {vector<bool> dp(s.size()+1,0);dp[0]=1;for(int i=1;i<=s.size();i++){bool ff=0;for(int j=0;j<a.size();j++){if(a[j].size()<=i){string t=s.substr(i-a[j].size(),a[j].size());if(t==a[j])ff|=dp[i-t.size()];}}dp[i]=ff;}return dp[s.size()];}
};

瘋狂的采藥(求組合數)

P1616 瘋狂的采藥 - 洛谷

采草藥,每種藥沒有限制,盡可能總價值越多越好;在這道題目中容易被草藥的價值所迷惑;仔細分析總時間是背包,采摘草藥所需的時間是物品,我們需要在時間里面分配采摘的情況使得草要的總價值最大;所以與上一題不同,這題需要物品在外,背包在內(求組合數)

DP五部曲:最大化價值

  1. 狀態定義:

    dp[j] 表示在時間限制 j 內能獲得的最大價值:

    • j 的取值范圍:0 到 t(總可用時間)

    • 目標:dp[t] 即時間限制 t 下的最大價值

  2. 狀態轉移:

    采用完全背包策略(每種藥材可采無限次):dp[j] = max(dp[j], dp[j - 采藥時間] + 藥材價值)

    轉移說明:

    • 不采當前藥材:保留原值 dp[j]
    • 采當前藥材:從 j - 采藥時間 轉移,加上新價值
    • 轉移方程
      dp[j] = max(dp[j], dp[j - a[i].fi] + a[i].se)
  3. 初始化:

    dp[0] = 0; // 0 時間內無法采藥,價值為 0
    dp[j] = 0; // 所有初始值設為 0(通過全局數組初始化實現)

  4. 遍歷順序:

    外層循環:遍歷藥材 i 從 1 到 m(物品維度)

    內層循環:順序枚舉時間ja[i].fit(容積維度)

    從低到高正序遍歷(完全背包特性)起點為當前藥材所需時間 a[i].fi

  5. 返回形式:輸出 dp[t] 即時間上限 t 時的最大價值

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
const int N=1e7+7;
int dp[N];
pii a[N];
void slove(){int t,m;cin>>t>>m;for(int i=1;i<=m;i++)cin>>a[i].fi>>a[i].se;for(int i=1;i<=m;i++){for(int j=a[i].fi;j<=t;j++)dp[j]=max(dp[j],dp[j-a[i].fi]+a[i].se);}cout<<dp[t];
} 
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}

自然數拆分

279. 自然數拆分 - AcWing題庫

趁熱打鐵,再來練一道;一樣的思路一樣的套路;這題的兩種循環效果是一樣的,不同順序視為相同(屬于相同的劃分方案)

DP五部曲:整數劃分問題(方案數統計)

  1. 狀態定義

    dp[j] 表示整數 j 可以被劃分的方案數(模 2147483648),包括:

    • 使用1到j的整數進行劃分
    • 允許重復使用相同的數
  2. 狀態轉移:

    轉移方程dp[j] = (dp[j] + dp[j-i]) % M

    • 含義:對于每個整數i,當前整數j的劃分方案數等于:

      不使用 i 的方案數(dp[j])加上使用至少一個 i 的方案數(dp[j-i]

  3. 初始化(邊界值)

    dp[0] = 1; // 空劃分:用0個數表示0的方案數為1
    dp[i] = 0; // 其他值初始化為0(通過vector初始化)

  4. 遍歷順序

    • 外層循環:枚舉使用的數 i 從 1 到 4003(覆蓋可能的n值)
    • 內層循環:枚舉整數 ji 到 4003(從小到大遍歷)
    • 順序要求:正序枚舉背包容量(完全背包模型)
  5. 返回形式

    • 輸入目標整數 n

    • 返回(dp[n] - 1) mod M其中:(減1表示排除只包含自身的情況)

      dp[n] 包含劃分成單個整數的方案

    • dp[n] 為0時,輸出 2147483647(即 M-1

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
const int N=4004;
const int M=2147483648;
int dp[N];
void slove(){int n;cin>>n;dp[0]=1;for(int i=1;i<=n;i++){for(int j=i;j<=n;j++){dp[j]=(dp[j]+dp[j-i])%M;}}int an=dp[n];if(an==0)an=M-1;elsean--;cout<<an<<endl;
} 
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}

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

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

相關文章

Linux驅動06 --- UDP

目錄 一、UDP 1.1 介紹 1.2 UDP 的通信方式 1.3 單播 發送函數 接收函數 1.4 廣播 1.5 組播/多播 一、UDP 1.1 介紹 傳輸層的另外一個協議 面向無連接&#xff0c;不穩定&#xff0c;速度快&#xff0c;可以一對多 UDP&#xff08;User Datagram Protocol&…

AJAX 投票:技術解析與應用場景

AJAX 投票:技術解析與應用場景 引言 隨著互聯網技術的不斷發展,Web應用的用戶體驗越來越受到重視。AJAX(Asynchronous JavaScript and XML)作為一種重要的技術,在實現異步數據交互方面發揮著關鍵作用。本文將深入探討AJAX投票系統的技術原理、應用場景以及優化策略。 A…

【字節跳動】數據挖掘面試題0017:推薦算法:雙塔模型,怎么把內容精準地推送給用戶

文章大綱 雙塔模型:推薦算法中的“高效匹配引擎一、雙塔模型的核心思想:“分而治之” 的匹配邏輯二、雙塔模型的結構:從特征輸入到相似度輸出1. 輸入層:特征的 “原材料處理”2. 塔網絡層:用戶與物品的“個性化編碼”3. 交互層:向量相似度的“偏好打分”三、雙塔模型的優…

7月14日日記

數學類今天考完最后一科英語放假回家了。有點羨慕他們。今天英語成績出來了&#xff0c;我是89分&#xff0c;一開始有點失望&#xff0c;感覺沒有上90&#xff0c;這是一個很好的沖擊4.0 的機會。但是后來一想好像也沒什么可惜的&#xff0c;這個分數還是很高的。舍友小林是90…

js的局部變量和全局變量

全局變量常常定義在函數外&#xff0c;具有全局定義域&#xff0c;在整個js代碼的任何地方都可以使用&#xff0c;這個就叫全局變量局部變量定義在函數內部&#xff0c;只在當前函數的定義域可以被使用&#xff0c;而且不同的函數可以定義相同的局部變量&#xff0c;他們之間相…

C++ 多態詳解:從概念到實現原理----《Hello C++ Wrold!》(14)--(C/C++)

文章目錄前言多態的概念多態的定義和實現虛函數虛函數的重寫(覆蓋)多態的構成條件override 和 final&#xff08;C11提出&#xff09;finaloverride重載、覆蓋(重寫)、隱藏(重定義)的對比抽象類接口繼承和實現繼承多態的原理虛函數表(也叫做虛表)引申:虛表的打印多態的原理靜態…

Node.js + Express的數據庫AB View切換方案設計

方案總覽數據導入過程&#xff1a; - 根據控制表判斷當前活躍組&#xff08;假設當前活躍的是a&#xff0c;那么接下來要導入到b&#xff09;。 - 清空非活躍表&#xff08;即b表&#xff09;的數據&#xff0c;然后將新數據導入到b表。 - 切換控制表&#xff0c;將活…

C++_編程提升_temaplate模板_案例

類模板案例案例描述: 實現一個通用的數組類&#xff0c;要求如下&#xff1a;可以對內置數據類型以及自定義數據類型的數據進行存儲將數組中的數據存儲到堆區構造函數中可以傳入數組的容量提供對應的拷貝構造函數以及operator防止淺拷貝問題提供尾插法和尾刪法對數組中的數據進…

Win11系統安裝Anaconda環境極簡教程

Win11系統安裝Anaconda環境極簡教程 &#x1f4e5; 第一步&#xff1a;下載 Anaconda 安裝包 打開瀏覽器&#xff0c;訪問 Anaconda 官網&#xff0c;選擇View All Installers 選擇所需版本&#xff08;此文以2024.02-1為例&#xff09;&#xff0c;點擊進行下載&#xff08;…

Datawhale AI夏令營-基于帶貨視頻評論的用戶洞察挑戰賽

一.賽事目標基于星火大模型Spark 4.0 Ultra&#xff0c;對視頻和評論的數據進行商品識別&#xff0c;情感分析&#xff0c;歸類分析&#xff0c;最終為帶貨效果進行評價。并通過優化模型來提高評價準確度二.賽事環境1.基礎平臺&#xff1a;星火大模型Spark 4.0 Ultra2.賽事數據…

如何基于FFMPEG 實現視頻推拉流

文章目錄 前言環境準備為什么選擇 FFmpeg什么是nginx 1.7.11.3 GryphonNginx的conf配置啟動nginx推流命令接收視頻流Untiy播放視頻流最后前言 我們經常會有在電腦上實現推拉流的需求,Unity 和Unreal 都提供了基于WebRTC 的視頻流方案,效果還不錯,但是當我們需要推拉整個電腦…

飛算JavaAI:從情緒價值到代碼革命,智能合并項目與定制化開發新范式

目錄一、飛算 JavaAI 是什么&#xff1f;二、飛算JavaAI&#xff1a;安裝登錄2.1 IDEA插件市場安裝&#xff08;推薦&#xff09;2.2 離線安裝包三、飛算JavaAI核心功能&#xff1a;一鍵生成完整工程代碼功能背景3.1 理解需求3.2 設計接口3.3 表結構自動設計3.4 處理邏輯&#…

Python 基礎語法與數據類型(十一) - 類 (class) 與對象 (實例)

文章目錄1. 什么是類 (Class)&#xff1f;1.1 定義一個類2. 什么是對象 (Object) 或實例 (Instance)&#xff1f;2.1 創建對象&#xff08;實例化&#xff09;3. 訪問屬性和調用方法4. 類屬性 vs 實例屬性5. self 的重要性總結練習題練習題答案前幾篇文章我們學習了變量、數據類…

精準數據檢索+數據飛輪自驅優化,彩訊AI知識庫助力企業知識賦能和效率創新

近兩年&#xff0c;人工智能技術的精細化發展&#xff0c;讓知識庫概念重新成為“熱門詞匯”&#xff0c;騰訊ima等智能工作臺產品為個人用戶打造專屬知識庫&#xff0c;而面向B端市場&#xff0c;企業AI知識庫也逐步成為企業集中存儲與管理核心文檔、數據、經驗和流程的知識中…

打破空間邊界!Nas-Cab用模塊化設計重構個人存儲邏輯

文章目錄前言1. Windows安裝Nas-Cab2. 本地局域網連接Nas-Cab3. 安裝Cpolar內網穿透4. 固定Nas-Cab 公網地址"數據管理不該受制于硬件形態或地理邊界。這個開源方案證明&#xff1a;當功能模塊化且可擴展時&#xff0c;私有云可以像水一樣滲透進所有設備——現在就去Git倉…

Sigma-Aldrich細胞培養基礎知識:細胞培養的安全注意事項

細胞培養實驗室風險評估風險評估的主要目的是防止人員受傷&#xff0c;保護財產&#xff0c;并避免對個人和環境的傷害。在許多國家&#xff0c;法律要求進行風險評估。例如&#xff0c;英國的《英國職業健康與安全法案&#xff08;1974年&#xff09;》就是一個例子。歐洲共同…

Imx6ull用網線與電腦連接

理解工作方式沒有路由器時&#xff0c;可以使用&#xff0c;只要保持虛擬機的兩個網卡一個與電腦在同一網,一個與板子在同一網段(保持通信)就可以從虛擬機往板子下載第一步&#xff1a;查看電腦連接的網絡這一步是在找到主機ip地址這兩步在其他同類教程里一樣的第二步:設置以太…

力扣454.四數相加Ⅱ

給你四個整數數組 nums1、nums2、nums3 和 nums4 &#xff0c;數組長度都是 n &#xff0c;請你計算有多少個元組 (i, j, k, l) 能滿足&#xff1a;0 < i, j, k, l < nnums1[i] nums2[j] nums3[k] nums4[l] 0示例 1&#xff1a;輸入&#xff1a;nums1 [1,2], nums2 …

Joplin:一款免費開源、功能強大且注重隱私的筆記軟件

Joplin 是一款免費開源、功能強大且注重隱私的筆記和待辦事項應用程序&#xff0c;它的設計目標是成為 Evernote 等流行筆記應用的強大替代品&#xff0c;尤其適合重視數據所有權和隱私的用戶。 功能特性 Joplin 的核心定位與優勢如下&#xff1a; 完全開源&#xff1a;代碼公…

滲透前四天總結

目錄 一.DNS DNS 基本概述 DNS解析過程 二.HTTPS TLS握手過程 RSA加密 對稱加密&#xff1a; 非對稱加密&#xff1a; RSA加密過程 三.使用xdebug調試php 四.信息收集 一.DNS DNS 基本概述 DNS&#xff1a;域名系統(DomainNameSystem)因特網的一項核心服務&#xf…