多重背包問題

来源:https://www.cnblogs.com/ycx-akioi/archive/2019/11/04/Multiple-knapsack.html
-Advertisement-
Play Games

多重背包問題 給定$n$種物品,第$i$種共有$c_i$個,價值為$v_i$,重量為$w_i$。現在有一個背包,最大載重量為$m$。求若選一些物品放到背包里,最多能放的總價值是多少。 解法$\bm1$ 考慮將多重背包轉化為01背包。最簡單的想法是將$1$種物品直接拆分成$c_i$個相同的物品,然後0 ...


多重背包問題

給定\(n\)種物品,第\(i\)種共有\(c_i\)個,價值為\(v_i\),重量為\(w_i\)。現在有一個背包,最大載重量為\(m\)。求若選一些物品放到背包里,最多能放的總價值是多少。

解法\(\bm1\)

考慮將多重背包轉化為01背包。最簡單的想法是將\(1\)種物品直接拆分成\(c_i\)個相同的物品,然後01背包。這樣時間複雜度是\(\mathrm O\left(\sum\limits_{i=1}^nc_i\cdot m\right)\),太大了。考慮有沒有更優的拆分方式。

我們先看這樣一個問題:給定一個數\(x\),問最少能選多少個數,使得\(\left[0,2^x\right)\)內每個數都能被表示成一些選出的數之和。很顯然可以選\(2^0,2^1,\cdots,2^{x-1}\)\(x\)個數,利用的是任何數都可以被二進位拆分這個原理。那麼如果給定一個數\(x\),問的是最少能選多少個數,使得\([0,x]\)內每個數都能被表示成一些選出的數之和呢?考慮找出\(x\)以內(包括\(x\))最大的一個能被表示成\(2\)的整次冪\(-1\)的數\(2^y-1\),那麼可以選\(y\)個數使得\(\left[0,2^y\right)\)內每個數都能被表示成一些選出的數之和(顯然\(y=\lfloor\log_2(x+1)\rfloor\))。那麼對於\(\left[2^y,x\right]\)內的數呢?只需要再添一個數\(x-2^y+1\)即可。因為\(\forall i\in\left[2^y,x\right]\),顯然\(i-\left(x-2^y+1\right)\in\left[2^{y+1}-x-1,2^y-1\right]\subseteq\left[0,2^y\right)\),那麼我們可以先湊出\(i-\left(x-2^y+1\right)\),再加一個\(x-2^y+1\)上去。

現在回到多重背包問題。第\(i\)種物品顯然可以被這樣拆分:\((2^0v_i,2^0w_i),(2^1v_i,2^1w_i),\cdots,\left(2^{\lfloor\log_2(c_i+1)\rfloor-1}v_i,2^{\lfloor\log_2(c_i+1)\rfloor-1}w_i\right),\left(\left(c_i-2^{\lfloor\log_2(c_i+1)\rfloor}+1\right)v_i,\left(c_i-2^{\lfloor\log_2(c_i+1)\rfloor}+1\right)w_i\right)\)(其中\((x,y)\)表示一個價值為\(x\),重量為\(y\)的物品)。這樣當且僅當\(j\in[0,c_i]\)時,\(j\)個第\(i\)種物品能被等價地表示出來,並且拆分成的物品的數量是\(\log\)級別的。於是這樣拆分完再01背包,複雜度就有保證了:\(\mathrm O\left(\sum\limits_{i=1}^n\log_2c_i\cdot m\right)\)。空間複雜度為拆分出來的物品個數+01背包所需空間:\(\mathrm O\left(\sum\limits_{i=1}^n\log_2c_i+m\right)\)

下麵貼代碼:

int nwn/*新物品個數*/,nwv[N*LOG_C_I+1]/*新物品價值*/,nww[N*LOG_C_I+1]/*新物品重量*/;
int dp[M+1];

nwn=0; 
for(int i=1;i<=n;i++){//拆分每種物品 
    int _log=log2(c[i]+1); 
    for(int j=0;j<_log;j++)nwv[++nwn]=v[i]<<j,nww[nwn]=w[i]<<j;//湊[0,2^_log) 
    if(c[i]>(1<<_log)-1)nwv[++nwn]=v[i]*(c[i]-(1<<_log)+1),nww[nwn]=w[i]*(c[i]-(1<<_log)+1);//湊[2^_log,c[i]] 
}
memset(dp,0,sizeof(dp));
for(int i=1;i<=nwn;i++)for(int j=m;j>=nww[i];j--)//01背包 
    dp[j]=max(dp[j],dp[j-nww[i]]+nwv[i]);
//目標為dp[m]

解法\(\bm2\)

直接DP。設\(dp_{i,j}\)表示當前考慮到第\(i\)個物品,背包容量還剩\(j\)時能放的最大價值。考慮枚舉第\(i\)個物品選了多少個,很容易得到轉移方程\(dp_{i,j}=\max\limits_{k=0}^{\min\left(c_i,\left\lfloor\frac j{w_i}\right\rfloor\right)}\left\{dp_{i-1,j-kw_i}+kv_i\right\}\)。這個方程不管是列法上還是長相上都一臉單調隊列的樣子。於是我們將它變變形,分離狀態變數\(j\)和決策變數\(k\)(其實就是改為枚舉剩餘容量),得\(dp_{i,j}=\max\limits_{k\in[\max(0,j-c_iw_i),j],k\equiv j\pmod{w_i}}\left\{dp_{i-1,k}-\dfrac k{w_i}v_i+\dfrac j{w_i}v_i\right\}\)。考慮到這裡面運算時會有小數,於是我們先加減後除,得\(dp_{i,j}=\max\limits_{k\in[\max(0,j-c_iw_i),j],k\equiv j\pmod{w_i}}\left\{\dfrac{w_idp_{i-1,k}-kv_i+jv_i}{w_i}\right\}\)

這樣就很顯然怎麼單調隊列了吧。註意到\(\max\)的條件里有一個同餘,於是我們可以把狀態變數\(j\)分為\(w_i\)組,每組中的成員分別\(\bmod w_i=0,1,\cdots,w_i-1\),每組單獨跑一遍單調隊列,維護當前狀態的所有決策中使\(w_idp_{i-1,k}-kv_i\)最大的決策\(k\)。而每到達一個\(j\),將決策\(k=j\)入隊並維護隊尾嚴格單調遞減性,將\(<\max(0,j-c_iw_i)\)的決策出隊即可。

這樣時間複雜度等於狀態數,為\(\mathrm O(nm)\),因為單調隊列是均攤\(\mathrm O(1)\)的。空間上呢,\(dp\)數組可以用滾動數組滾掉第一維,於是空間複雜度為\(\mathrm O(n+m)\)

下麵貼代碼:

int q[M],head,tail;//單調隊列 
int dp[2][M+1]; 

memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)//考慮到第i個物品 
    for(int j=0;j<w[i];j++){//分組考慮 
        head=tail=0;//清空隊列 
        for(int k=j;k<=m;k+=w[i]){//遍歷此組中的所有狀態 
            while(head<tail&&q[head]<k-c[i]*w[i])head++;//出隊 
            while(head<tail&&w[i]*dp[i-1&1][q[tail-1]]-q[tail-1]*v[i]<=w[i]*dp[i-1&1][k]-k*v[i])tail--;//維護隊尾單調性 
            q[tail++]=k;//入隊 
            dp[i&1][k]=(w[i]*dp[i-1&1][q[head]]-q[head]*v[i]+k*v[i])/w[i];//此時隊首是最優決策 
        }
    }
//目標為dp[n&1][m]

\(\bm2\)兩種解法的比較

複雜度上,不管是時間還是空間,都是解法\(2\)更優。不過單調隊列相對難推、難寫,要是在比賽中,不卡時間不卡常的話,還是寫解法\(1\)為妙。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • jQuery的DOM遍歷模塊對DOM模型的原生屬性parentNode、childNodes、firstChild、lastChild、previousSibling、nextSibling進行了封裝和擴展,用於在DOM樹中遍歷父元素、子元素和兄弟元素。 可以通過jQuery的實例來訪問,方法如下: ...
  • "洛谷題目頁面傳送門" & "CodeForces題目頁面傳送門" 定義一個$1\sim n$的排列$a$的平方$a^2=b$,當且僅當$\forall i\in[1,n],b_i=a_{a_i}$,即$a^2$為將$a$在$[1,2,\cdots,n]$上映射$2$次所得的排列。現在給定一個$1\ ...
  • 2019-11-04-23:03:13 目錄: 1.常用的數據結構 2.棧 3.隊列 4.數組 5.鏈表 6.紅黑樹 常用的數據結構: 包含:棧、隊列、數組、鏈表和紅黑樹 棧: 棧:stack,又稱堆棧,它是運算受限的線性表,其限制是僅允許在標的一端進行插入和刪除操作,不允許在其 他任何位置進行添加 ...
  • 進程:通俗理解一個運行的程式或者軟體,進程是操作系統資源分配的基本單位 1.1、導入進程模塊 import multiprocessing 1.2、Process進程類的語法結構如下: Process([group[, target[, name[,args[,kwargs]]]]]) group: ...
  • 本文經授權轉自公眾號:石杉的架構筆記 一、問題起源 二、Eureka Server設計精妙的註冊表存儲結構 三、Eureka Server端優秀的多級緩存機制 四、總結 一、問題起源 Spring Cloud架構體系中,Eureka是一個至關重要的組件,它扮演著微服務註冊中心的角色,所有的服務註冊與 ...
  • pom.xml 華為雲鏡像: -基本web開發 2.安裝Lombok插件:plugins >lombok 3.實體類中 4.Controller: 請求參數兩種類型: @RequestParam 獲取查詢參數。即url?name=value 這種形式 @PathVariable 獲取路徑參數。即ur ...
  • 前言:之前有寫過一篇關於LRU的文章鏈接https://www.cnblogs.com/wyq178/p/9976815.html LRU全稱:Least Recently Used:最近最少使用策略,判斷最近被使用的時間,距離目前最遠的數據優先被淘汰,作為一種根據訪問時間來更改鏈表順序從而實現緩存 ...
  • 本篇文章給大家帶來的內容是關於Laravel框架下路由的使用(源碼解析),有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。 前言 我的解析文章並非深層次多領域的解析攻略。但是參考著開發文檔看此類文章會讓你在日常開發中更上一層樓。 廢話不多說,我們開始本章的講解。 入口 Laravel啟 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...