【背包dp-----分組背包】------(標準的分組背包【可以不裝滿的 最大價值】)

通天之分組背包

題目鏈接

題目描述

01 01 01 背包問世之后,小 A 對此深感興趣。一天,小 A 去遠游,卻發現他的背包不同于 01 01 01 背包,他的物品大致可分為 k k k 組,每組中的物品相互沖突,現在,他想知道最大的利用價值是多少。

輸入格式

兩個數 m , n m,n m,n,表示一共有 n n n 件物品,總重量為 m m m

接下來 n n n 行,每行 3 3 3 個數 a i , b i , c i a_i,b_i,c_i ai?,bi?,ci?,表示物品的重量,利用價值,所屬組數。

輸出格式

一個數,最大的利用價值。

輸入輸出樣例 #1

輸入 #1

45 3
10 10 1
10 5 1
50 400 2

輸出 #1

10

說明/提示

0 ≤ m ≤ 1000 0 \leq m \leq 1000 0m1000 1 ≤ n ≤ 1000 1 \leq n \leq 1000 1n1000 1 ≤ k ≤ 100 1\leq k\leq 100 1k100 a i , b i , c i a_i, b_i, c_i ai?,bi?,ci?int 范圍內。

\

0、分組背包特點

  • 每組內只能選 一個物品 或者 不選任何物品
  • 每組之間是獨立的,可以按順序處理;
  • 對每組分別進行一次 0-1 背包更新;
  • 最終使用動態規劃來記錄狀態。

題解(二維dp)


一、狀態定義

我們使用二維動態規劃數組:

dp[i][j] 表示從前 i物品組中,在總容量恰好為 j 的情況下所能獲得的最大價值。


二、 初始化分析

for(ll j=0;j<=m;j++)
{dp[0][j]=0;
}
  • 第 0 組表示“沒有物品可選”,此時無論容量是多少,最大價值都為 0;
  • 其他位置初始化為 LLONG_MIN,表示不可達狀態。

三、狀態轉移方程

對于當前組中的每一個物品 {w, v}(重量和價值),我們嘗試將其加入到容量 j 的背包中:

if (j >= w) 
{dp[i][j] = max(dp[i][j], dp[i - 1][j - w] + v);
}

這個狀態轉移的意思是:

  • 如果當前容量 j 可以裝下當前物品,則從上一組的容量 j - w 狀態中轉移過來,并加上該物品的價值;
  • 否則保持不變(繼承上一組的狀態)。

四、實現細節

1.數據結構說明

map<ll, vector<pair<ll, ll>>> groups;
  • 使用 map 來將物品按組號分類;
  • 每個組是一個 vector<pair<weight, value>>,存儲該組中的所有物品。

2.動態規劃數組初始化

vector<vector<ll>> dp(group_count + 1, vector<ll>(m + 1, LLONG_MIN));
for(ll j=0;j<=m;j++)
{dp[0][j]=0;
}
  • 初始時,除了第 0 組外,其他所有狀態設為最小值 LLONG_MIN
  • 第 0 組初始化為全 0,表示不選任何物品時的價值。

3.遍歷過程的主次順序

3.1. 外層循環:遍歷物品組
  • 主要任務:遍歷每一個物品組(從第1組開始,跳過第0組)。
  • 實現方式
    for (auto it = (++groups.begin()); it != groups.end(); it++) 
    {ll gid = it->first;const vector<pair<ll, ll>>& items = it->second;// ...
    }
    
  • 說明:這里的 it 是指向當前物品組的迭代器,gid 是當前組的組號,items 是該組內所有物品的集合。
3.2. 繼承上一組的狀態
  • 主要任務:在處理當前組之前,首先繼承前一組的狀態(即假設不選擇當前組中的任何物品)。
  • 實現方式
    for (ll j = 0; j <= m; j++) 
    {dp[gid][j] = dp[gid - 1][j];
    }
    
  • 說明這一步確保了即使當前組沒有合適的物品可選或者我們決定不選當前組的任何物品時,我們的解不會變差。
3.3. 中層循環:遍歷當前組內的每個物品
  • 主要任務:對于當前組中的每一個物品,嘗試將其加入背包,并更新相應的狀態。
  • 實現方式
    for (const auto& item : items) {ll w = item.first;  // 當前物品的重量ll v = item.second; // 當前物品的價值// ...
    }
    
3.4. 最內層循環:遍歷背包容量
  • 主要任務:對于當前物品,遍歷所有可能的背包容量 j,并根據當前物品的重量和價值更新 dp 數組。
  • 實現方式
    for (ll j = w; j <= m; j++) 
    { // 注意這里從 w 開始循環if (dp[gid - 1][j - w] != LLONG_MIN){dp[gid][j] = max(dp[gid][j], dp[gid - 1][j - w] + v);}
    }
    
  • 說明這里從 w 開始循環是因為只有當背包容量大于等于當前物品的重量時,才有可能將該物品放入背包。
3.5.總結遍歷過程的主次順序
  1. 外層循環:遍歷物品組

    • 對于每一個物品組(從第1組開始),獲取該組的所有物品。
  2. 繼承上一組的狀態

    • 在處理當前組之前,先繼承前一組的狀態(即假設不選擇當前組中的任何物品),確保基礎狀態正確。
  3. 中層循環:遍歷當前組內的每個物品

    • 對于當前組中的每一個物品,計算其對背包的影響。
  4. 最內層循環:遍歷背包容量

    • 對于當前物品,遍歷所有可能的背包容量,并根據當前物品的重量和價值更新 dp 數組。

五、完整代碼

#include <bits/stdc++.h>
using namespace std;
using ll = long long;int main() 
{// m 表示背包的最大容量// n 表示物品總數ll m, n;cin >> m >> n;// 使用 map 將物品按組號分類存儲// groups[group_id] 存儲該組下的所有物品 {weight, value}map<ll, vector<pair<ll, ll>>> groups;// 插入一個無效的第0組,使后續組號可以直接和 dp 數組下標對應groups[0].push_back({0, 0});// 輸入每個物品的信息:重量、價值、所屬組號for (ll i = 0; i < n; i++) {ll weight, value, group_id;cin >> weight >> value >> group_id;groups[group_id].push_back({weight, value});}// 動態規劃數組 dp[i][j]// dp[i][j] 表示前 i 組物品,在容量 j 下可以獲得的最大價值// 初始化為 LLONG_MIN 表示不可達狀態ll group_count = groups.size() - 1; // 減去人為添加的第0組vector<vector<ll>> dp(group_count + 1, vector<ll>(m + 1, LLONG_MIN));// 初始狀態:前 0 組(沒有選任何物品)時,所有容量下的最大價值都是 0for (ll j = 0; j <= m; j++) {dp[0][j] = 0;}// 遍歷每一組(從實際的第1組開始)// 注意這里使用 ++groups.begin() 跳過第0組for (auto it = (++groups.begin()); it != groups.end(); it++) {ll gid = it->first;                     // 當前組號const vector<pair<ll, ll>>& items = it->second; // 該組的所有物品// 先繼承上一組的狀態(不選當前組中的任何物品)for (ll j = 0; j <= m; j++) {dp[gid][j] = dp[gid - 1][j];}// 對當前組中的每一個物品嘗試選擇for (const auto& item : items) {ll w = item.first;   // 當前物品的重量ll v = item.second;  // 當前物品的價值// 遍歷所有可能的背包容量 j,從當前物品的重量開始for (ll j = w; j <= m; j++) {// 如果上一組 j - w 容量是可達的,則更新當前組 j 容量下的最大價值if (dp[gid - 1][j - w] != LLONG_MIN) {dp[gid][j] = max(dp[gid][j], dp[gid - 1][j - w] + v);}}}}// 輸出結果:最后一組在最大容量 m 下所能獲得的最大價值cout << dp[group_count][m] << endl;return 0;
}

六、 時間與空間復雜度分析

類型復雜度
時間復雜度 O ( G ? m ? K ) O(G \cdot m \cdot K) O(G?m?K)
空間復雜度 O ( G ? m ) O(G \cdot m) O(G?m)

其中:

  • G G G:物品組數;
  • m m m:背包容量;
  • K K K:平均每組中的物品數量。

題解(滾動數組優化)

一、 動態規劃數組 dp[j]

  • 定義dp[j] 表示在容量為 j 的情況下可以獲得的最大價值。
  • 初始化:所有值初始化為 LLONG_MIN(表示不可達狀態),除了 dp[0] = 0(容量為 0 時的最大價值為 0)。

2. 遍歷主次順序

2.1主循環:遍歷每一組物品
for (auto it = groups.begin(); it != groups.end(); it++) 
{const vector<pair<ll, ll>>& items = it->second;
  • 遍歷順序:我們首先遍歷每一個物品組,從第一個組到最后一個組依次處理。
  • 原因:這樣可以確保每組的狀態轉移依賴于前一組的狀態,從而逐步構建最終的最優解。
2.2次循環:倒序處理容量 j
vector<ll> temp_dp(dp); // 先繼承上一組的狀態for (ll j = m; j >= 0; j--) 
{for (const auto& item : items) {ll weight = item.first;ll value = item.second;if (j >= weight && dp[j - weight] != LLONG_MIN) {temp_dp[j] = max(temp_dp[j], dp[j - weight] + value);}}
}
  • 倒序處理容量 j:從大到小遍歷容量 j,確保每次更新不會覆蓋尚未使用的舊值。
  • 原因:因為在動態規劃中,我們需要基于未被當前操作修改過的舊值進行計算,倒序遍歷可以避免數據覆蓋的問題。

3. 使用臨時 dp 數組

vector<ll> temp_dp(dp); // 先繼承上一組的狀態
  • 為什么需要臨時 dp 數組

    • 在處理每一組時,我們需要先保存上一組的狀態(即假設不選擇當前組中的任何物品)。
    • 如果直接在 dp 上進行更新,會導致后續的操作基于已經被修改的狀態,從而影響結果的正確性。
    • 使用臨時數組 temp_dp 可以確保我們在處理當前組的所有物品之前,已經完整地保存了上一組的狀態。
  • 合并 temp_dpdp

    • 在處理完當前組的所有物品之后,我們將 temp_dp 合并回 dp,以完成狀態轉移。

4. 輸出結果

cout << *max_element(dp.begin(), dp.end()) << endl;
  • 輸出最大價值:由于我們的目標是找到最大可能的價值,而不是僅限于給定容量 m,所以我們使用 *max_element(dp.begin(), dp.end()) 來獲取整個 dp 數組中的最大值。

5.完整代碼

#include <bits/stdc++.h>
using namespace std;
using ll = long long;int main() 
{// 輸入 m 表示背包的最大容量// n 表示物品總數ll m, n; cin >> m >> n;// 使用 map 存儲每組物品:// group_id -> vector<pair<weight, value>> 表示每個組中的所有物品map<ll, vector<pair<ll, ll>>> groups;// 讀取每個物品的信息:重量、價值、所屬組號for (ll i = 0; i < n; i++) {ll weight, value, group_id;cin >> weight >> value >> group_id;groups[group_id].push_back(make_pair(weight, value));}// 動態規劃數組 dp[j]:// 表示當前狀態下,容量為 j 時可以獲得的最大價值// 初始值設為 LLONG_MIN 表示不可達的狀態// dp[0] 初始化為 0 表示容量為 0 時的價值為 0vector<ll> dp(m + 1, LLONG_MIN);dp[0] = 0;// 遍歷每一組物品for (auto it = groups.begin();it != groups.end(); ++it) {// 當前組的所有物品列表const vector<pair<ll, ll>>& items = it->second;// 創建 temp_dp 作為當前組的臨時狀態數組// temp_dp 初始化為當前 dp 的值(繼承上一組的狀態)// 這樣做是為了保證我們可以不選當前組中的任何物品vector<ll> temp_dp(dp);// 倒序遍歷容量 j(從大到小)// 原因:防止在更新 temp_dp[j] 時,前面已經計算過的結果被覆蓋for (ll j = m; j >= 0; j--) {// 嘗試從當前組中選擇每一個物品for (const auto& item : items) {ll weight = item.first;ll value = item.second;// 如果當前容量 j 足夠裝下這個物品,并且 j - weight 狀態可達if (j >= weight && dp[j - weight] != LLONG_MIN) {// 更新 temp_dp[j] 為:// 前一組容量 j - weight 的最大價值 + 當前物品價值// 取 max 是因為可能有多個物品競爭同一個 j 容量temp_dp[j] = max(temp_dp[j], dp[j - weight] + value);}}}// 將 temp_dp 合并回 dp 中,表示處理完這一組后的最終狀態dp = temp_dp;}// 輸出結果:整個 dp 數組中的最大值(不一定剛好用滿容量 m)cout << *max_element(dp.begin(), dp.end()) << endl;return 0;
}

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

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

相關文章

操作系統:os概述

操作系統&#xff1a;OS概述 程序、進程與線程無極二級目錄三級目錄 程序、進程與線程 指令執行需要那些條件&#xff1f;CPU內存 需要數據和 無極 二級目錄 三級目錄

RAG文本分塊

不論是向量化模型還是大語言模型&#xff0c;都存在輸入長度的限制。對于超過限制的文本&#xff0c;模型會進行截斷&#xff0c;造成語義缺失。分塊可以確保每個文本片段都在模型的處理范圍內&#xff0c;避免重要信息的丟失。 文本分塊的核心原則 高質量分塊的核心原則是&a…

2025 年九江市第二十三屆中職學校技能大賽 (網絡安全)賽項競賽樣題

2025 年九江市第二十三屆中職學校技能大賽 &#xff08;網絡安全&#xff09;賽項競賽樣題 &#xff08;二&#xff09;A 模塊基礎設施設置/安全加固&#xff08;200 分&#xff09;A-1 任務一登錄安全加固&#xff08;Windows,Linux&#xff09;A-2 任務二 Nginx 安全策略&…

量子隧穿:PROFINET到Ethernet ip的無損耗協議轉換方案轉

在本季度的生產工作中&#xff0c;我們成功實現了倉儲物流自動化分揀系統中的關鍵技術突破。我們面臨的主要挑戰是將采用EtherNet/IP協議的輸送帶控制器與PROFINET協議的上位系統進行有效通信。通過引入ethernet IP轉PROFINET網關倍訊科技BX-606-EIP&#xff0c;我們實現了輸送…

OpenCV CUDA模塊中矩陣操作------降維操作

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 cv::cuda::reduce 函數用于對 GPU 上的矩陣沿某個維度進行降維操作&#xff0c;例如求和、取最大值等。此函數支持多種降維操作&#xff0c;并允…

一分鐘用 MCP 上線一個 貪吃蛇 小游戲(CodeBuddy版)

我正在參加CodeBuddy「首席試玩官」內容創作大賽&#xff0c;本文所使用的 CodeBuddy 免費下載鏈接&#xff1a;騰訊云代碼助手 CodeBuddy - AI 時代的智能編程伙伴 你好&#xff0c;我是悟空。 背景 上篇我們用 MCP 上線了一個 2048 小游戲&#xff0c;這次我們繼續做一個 …

簡單神經網絡(ANN)實現:從零開始構建第一個模型

本文將手把手帶你用 Python Numpy 實現一個最基礎的人工神經網絡&#xff08;Artificial Neural Network, ANN&#xff09;。不依賴任何深度學習框架&#xff0c;適合入門理解神經網絡的本質。 一、項目目標 構建一個三層神經網絡&#xff08;輸入層、隱藏層、輸出層&#xf…

使用python進行人員軌跡跟蹤

一、系統概述 該系統基于計算機視覺技術&#xff0c;實現對視頻或攝像頭畫面中的人員進行檢測、跟蹤&#xff0c;并生成軌跡數據。支持透視變換校準&#xff08;鳥瞰圖顯示&#xff09;、多目標跟蹤、軌跡存儲及視頻錄制功能&#xff0c;適用于安防監控、行為分析等場景。 二…

[強化學習的數學原理—趙世鈺老師]學習筆記02-貝爾曼方程

本人為強化學習小白&#xff0c;為了在后續科研的過程中能夠較好的結合強化學習來做相關研究&#xff0c;特意買了西湖大學趙世鈺老師撰寫的《強化學習數學原理》中文版這本書&#xff0c;并結合趙老師的講解視頻來學習和更深刻的理解強化學習相關概念&#xff0c;知識和算法技…

Docker入門指南:鏡像、容器與倉庫的核心概念解析

目錄 前言&#xff1a;為什么需要Docker&#xff1f; 一、Docker能做什么&#xff1f; 二、核心概念解析 1. 鏡像&#xff08;Image&#xff09;&#xff1a;應用的標準化打包 2. 容器&#xff08;Container&#xff09;&#xff1a;鏡像的運行實例 3. 鏡像倉庫&#xff0…

大模型微調實戰:基于GpuGeek平臺的低成本高效訓練方案

文章目錄 引言一、GpuGeek平臺使用入門1. 注冊與賬號設置2. 控制臺功能概覽3. 快速創建GPU實例3. 預置鏡像與自定義環境 二、GpuGeek平臺核心優勢解析1. 顯卡資源充足&#xff1a;多卡并行加速訓練2. 鏡像超多&#xff1a;開箱即用的開發環境3. 計費靈活&#xff1a;按需付費降…

Linux:計算機的層狀結構

1.馮諾依曼體系結構 我們常見的計算機&#xff0c;如筆記本、臺式機。我們不常見的計算機&#xff0c;如服務器&#xff0c;大部分都遵守馮諾依曼體系結構。 CPU&#xff1a;運算器和控制器組成。運算器主要工作是做算術運算和邏輯運算。控制器主要工作是協調設備之間信息流動的…

LangGraph(四)——加入人機交互控制

目錄 1. 引言2. 添加Human Assistance工具3. 編譯狀態圖4. 提示聊天機器人5. 恢復執行參考 1. 引言 智能體可能不可靠&#xff0c;甚至需要人工輸入才能完成任務。同樣&#xff0c;對于某些操作&#xff0c;你可能需要在運行前獲得人工批準&#xff0c;以保證一切按預期運行。 …

數據結構【AVL樹】

AVL樹 1.AVL樹1.AVL的概念2.平衡因子 2.AVl樹的實現2.1AVL樹的結構2.2AVL樹的插入2.3 旋轉2.3.1 旋轉的原則 1.AVL樹 1.AVL的概念 AVL樹可以是一個空樹。 它的左右子樹都是AVL樹&#xff0c;且左右子樹的高度差的絕對值不超過1。AVL樹是一顆高度平衡搜索二叉樹&#xff0c;通…

JavaScript【5】DOM模型

1.概述&#xff1a; DOM (Document Object Model)&#xff1a;當頁面被加載時&#xff0c;瀏覽器會創建頁面的文檔對象模型&#xff0c;即dom對象&#xff1b;dom對象會被結構化為對象樹&#xff0c;如一個HTML文檔會被分為head&#xff0c;body等部分&#xff0c;而每個部分又…

STM32燒錄程序正常,但是運行異常

一、硬件配置問題 BOOT引腳設置錯誤 STM32的啟動模式由BOOT0和BOOT1引腳決定。若設置為從RAM啟動&#xff08;BOOT01&#xff0c;BOOT10&#xff09;&#xff0c;程序在掉電后無法保存&#xff0c;導致復位后無法正常運行。應確保BOOT00&#xff08;從Flash啟動&#xff09;15。…

汽車二自由度系統模型以及電動助力轉向系統模型

汽車二自由度系統模型與電動助力轉向系統&#xff08;EPS&#xff09;的詳細建模方案&#xff0c;包含理論推導、MATLAB/Simulink實現代碼及參數說明&#xff1a; 一、二自由度汽車模型 1. 模型描述 包含以下兩個自由度&#xff1a; 橫向運動&#xff08;側向加速度&#xf…

git提交庫常用詞

新功能 feat修改BUG fix文檔修改 docs格式修改 style重構 refactor性能提升 perf測試 test構建系統 build對CI配置文件修改 ci修改構建流程、或增加依賴庫、工具 chore回滾版本 revert

JavaScript 時間轉換:從 HH:mm:ss 到十進制小時及反向轉換

關鍵點 JavaScript 可以輕松實現時間格式&#xff08;HH:mm:ss 或 HH:mm&#xff09;與十進制小時&#xff08;如 17.5&#xff09;的相互轉換。兩個函數分別處理時間字符串到十進制小時&#xff0c;以及十進制小時到時間字符串的轉換&#xff0c;支持靈活的輸入和輸出格式。這…

LLM智能體新紀元:深入解析MCP與A2A協議,賦能智能自動化協作

LLM智能體&#xff08;LLM agents&#xff09;是能夠自主行動以實現特定目標的AI系統。在實際應用中&#xff0c;智能體能夠將用戶請求拆解為多個步驟&#xff0c;利用知識庫或API獲取數據&#xff0c;最終整合出答案。這讓智能體相比于傳統獨立聊天機器人擁有更強大的能力——…