背包問題(動態規劃)

本篇文章作為個人的背包問題學習資料,來自轉載 dd大牛的《背包九講》.

P01: 01背包問題

題目
有N件物品和一個容量為V的背包。第i件物品的費用是c[i],價值是w[i]。求解將哪些物品裝入背包可使這些物品的費用總和不超過背包容量,且價值總和最大。

基本思路
這是最基礎的背包問題,特點是:每種物品僅有一件,可以選擇放或不放。

用子問題定義狀態:即f[i][v]表示前i件物品恰放入一個容量為v的背包可以獲得的最大價值。則其狀態轉移方程便是:f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}。

這個方程非常重要,基本上所有跟背包相關的問題的方程都是由它衍生出來的。所以有必要將它詳細解釋一下:“將前i件物品放入容量為v的背包中”這個子問題,若只考慮第i件物品的策略(放或不放),那么就可以轉化為一個只牽扯前i-1件物品的問題。如果不放第i件物品,那么問題就轉化為“前i-1件物品放入容量為v的背包中”;如果放第i件物品,那么問題就轉化為“前i-1件物品放入剩下的容量為v-c[i]的背包中”,此時能獲得的最大價值就是f [i-1][v-c[i]]再加上通過放入第i件物品獲得的價值w[i]。

注意f[i][v]有意義當且僅當存在一個前i件物品的子集,其費用總和為v。所以按照這個方程遞推完畢后,最終的答案并不一定是f[N] [V],而是f[N][0..V]的最大值。如果將狀態的定義中的“恰”字去掉,在轉移方程中就要再加入一項f[i][v-1],這樣就可以保證f[N] [V]就是最后的答案。至于為什么這樣就可以,由你自己來體會了。

優化空間復雜度
以上方法的時間和空間復雜度均為O(N*V),其中時間復雜度基本已經不能再優化了,但空間復雜度卻可以優化到O(V)。

先考慮上面講的基本思路如何實現,肯定是有一個主循環i=1..N,每次算出來二維數組f[i][0..V]的所有值。那么,如果只用一個數組f [0..V],能不能保證第i次循環結束后f[v]中表示的就是我們定義的狀態f[i][v]呢?f[i][v]是由f[i-1][v]和f[i-1] [v-c[i]]兩個子問題遞推而來,能否保證在推f[i][v]時(也即在第i次主循環中推f[v]時)能夠得到f[i-1][v]和f[i-1][v -c[i]]的值呢?事實上,這要求在每次主循環中我們以v=V..0的順序推f[v],這樣才能保證推f[v]時f[v-c[i]]保存的是狀態f[i -1][v-c[i]]的值。偽代碼如下:

for i=1..N
for v=V..0
f[v]=max{f[v],f[v-c[i]]+w[i]};

其中的f[v]=max{f[v],f[v-c[i]]}一句恰就相當于我們的轉移方程f[i][v]=max{f[i-1][v],f[i- 1][v-c[i]]},因為現在的f[v-c[i]]就相當于原來的f[i-1][v-c[i]]。如果將v的循環順序從上面的逆序改成順序的話,那么則成了f[i][v]由f[i][v-c[i]]推知,與本題意不符,但它卻是另一個重要的背包問題P02最簡捷的解決方案,故學習只用一維數組解01背包問題是十分必要的。

總結
01背包問題是最基本的背包問題,它包含了背包問題中設計狀態、方程的最基本思想,另外,別的類型的背包問題往往也可以轉換成01背包問題求解。故一定要仔細體會上面基本思路的得出方法,狀態轉移方程的意義,以及最后怎樣優化的空間復雜度。

P02: 完全背包問題

題目
有N種物品和一個容量為V的背包,每種物品都有無限件可用。第i種物品的費用是c[i],價值是w[i]。求解將哪些物品裝入背包可使這些物品的費用總和不超過背包容量,且價值總和最大。

基本思路
這個問題非常類似于01背包問題,所不同的是每種物品有無限件。也就是從每種物品的角度考慮,與它相關的策略已并非取或不取兩種,而是有取0件、取1件、取2件……等很多種。如果仍然按照解01背包時的思路,令f[i][v]表示前i種物品恰放入一個容量為v的背包的最大權值。仍然可以按照每種物品不同的策略寫出狀態轉移方程,像這樣:f[i][v]=max{f[i-1][v-k*c[i]]+kw[i]|0<=kc[i]<= v}。這跟01背包問題一樣有O(N*V)個狀態需要求解,但求解每個狀態的時間則不是常數了,求解狀態f[i][v]的時間是O(v/c[i]),總的復雜度是超過O(VN)的。

將01背包問題的基本思路加以改進,得到了這樣一個清晰的方法。這說明01背包問題的方程的確是很重要,可以推及其它類型的背包問題。但我們還是試圖改進這個復雜度。

一個簡單有效的優化
完全背包問題有一個很簡單有效的優化,是這樣的:若兩件物品i、j滿足c[i]<=c[j]且w[i]>=w[j],則將物品j去掉,不用考慮。這個優化的正確性顯然:任何情況下都可將價值小費用高得j換成物美價廉的i,得到至少不會更差的方案。對于隨機生成的數據,這個方法往往會大大減少物品的件數,從而加快速度。然而這個并不能改善最壞情況的復雜度,因為有可能特別設計的數據可以一件物品也去不掉。

轉化為01背包問題求解
既然01背包問題是最基本的背包問題,那么我們可以考慮把完全背包問題轉化為01背包問題來解。最簡單的想法是,考慮到第i種物品最多選V/c [i]件,于是可以把第i種物品轉化為V/c[i]件費用及價值均不變的物品,然后求解這個01背包問題。這樣完全沒有改進基本思路的時間復雜度,但這畢竟給了我們將完全背包問題轉化為01背包問題的思路:將一種物品拆成多件物品。

更高效的轉化方法是:把第i種物品拆成費用為c[i]2^k、價值為w[i]2^k的若干件物品,其中k滿足c[i]2^k<V。這是二進制的思想,因為不管最優策略選幾件第i種物品,總可以表示成若干個2^k件物品的和。這樣把每種物品拆成O(log(V/c[i]))件物品,是一個很大的改進。但我們有更優的O(VN)的算法。 O(VN)的算法這個算法使用一維數組,先看偽代碼: <pre class"example"> for i=1..N for v=0..V f[v]=max{f[v],f[v-c[i]]+w[i]};

你會發現,這個偽代碼與P01的偽代碼只有v的循環次序不同而已。為什么這樣一改就可行呢?首先想想為什么P01中要按照v=V..0的逆序來循環。這是因為要保證第i次循環中的狀態f[i][v]是由狀態f[i-1][v-c[i]]遞推而來。換句話說,這正是為了保證每件物品只選一次,保證在考慮“選入第i件物品”這件策略時,依據的是一個絕無已經選入第i件物品的子結果f[i-1][v-c[i]]。而現在完全背包的特點恰是每種物品可選無限件,所以在考慮“加選一件第i種物品”這種策略時,卻正需要一個可能已選入第i種物品的子結果f[i][v-c[i]],所以就可以并且必須采用v= 0..V的順序循環。這就是這個簡單的程序為何成立的道理。

這個算法也可以以另外的思路得出。例如,基本思路中的狀態轉移方程可以等價地變形成這種形式:f[i][v]=max{f[i-1][v],f[i][v-c[i]]+w[i]},將這個方程用一維數組實現,便得到了上面的偽代碼。

總結
完全背包問題也是一個相當基礎的背包問題,它有兩個狀態轉移方程,分別在“基本思路”以及“O(VN)的算法“的小節中給出。希望你能夠對這兩個狀態轉移方程都仔細地體會,不僅記住,也要弄明白它們是怎么得出來的,最好能夠自己想一種得到這些方程的方法。事實上,對每一道動態規劃題目都思考其方程的意義以及如何得來,是加深對動態規劃的理解、提高動態規劃功力的好方法。

轉載于:https://www.cnblogs.com/A-Little-Nut/p/8372451.html

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

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

相關文章

notepad編譯java_Notepad++直接編譯運行java代碼的具體步驟

最近不少朋友表示還不會Notepad直接編譯運行java代碼的操作步驟&#xff0c;使用下面小編就帶來了Notepad直接編譯運行java代碼的操作方法哦&#xff0c;一起去看看吧。Notepad直接編譯運行java代碼的具體步驟下載Notepad&#xff0c;找到Plugin Manager。插件--->Plugin Ma…

基于linux 的2048

于 debian 接著寫 2048, 的影響&#xff0c;如下面的&#xff1a; 感興趣的朋友能夠在這里&#xff08;http://download.csdn.net/download/kamsau/7330933&#xff09;下載。 版權聲明&#xff1a;本文博客原創文章&#xff0c;博客&#xff0c;未經同意&#xff0c;不得轉載。…

架構師之路

1. 架構師之路(1)---面向過程和面向對象 1、引言 機算機科學是一門應用科學&#xff0c;它的知識體系是典型的倒三角結構&#xff0c;所用的基礎知識并不多&#xff0c;只是隨著應用領域和方向的不同&#xff0c;產生了很多的分支&#xff0c;所以說編程并不是一件很困難的…

r語言做斷軸_R語言用nls做非線性回歸以及函數模型的參數估計

非線性回歸是在對變量的非線性關系有一定認識前提下&#xff0c;對非線性函數的參數進行最優化的過程&#xff0c;最優化后的參數會使得模型的RSS(殘差平方和)達到最小。在R語言中最為常用的非線性回歸建模函數是nls&#xff0c;下面以car包中的USPop數據集為例來講解其用法。數…

day8-異常處理與網絡編程

第1章 異常處理 1.1 什么是異常? 1.1.1 描述 #1 什么是異常&#xff1f; # 異常是錯誤發生的信號&#xff0c;一旦程序出錯&#xff0c;就會產生一個異常&#xff0c;應用程序未處理該異常&#xff0c; # 異常便會拋出&#xff0c;程序隨之終止 異常就是程序運行時發生錯誤的信…

常用數據結構的一部分類

VECTORvector是可以實現自動增長的對象數組。java.util.vector提供了向量類&#xff08;vector&#xff09;來實現向量數組的功能。在C和C中可以使用指針來實現動態數組&#xff0c;java通過提供大量的類庫來彌補這個功能。向量類的對象 可以向其中隨意插入不同類的對象&#x…

進程(并發,并行) join start 進程池 (同步異步)

一、背景知識 顧名思義&#xff0c;進程即正在執行的一個過程。進程是對正在運行程序的一個抽象。進程的概念起源于操作系統&#xff0c;是操作系統最核心的概念&#xff0c;也是操作系統提供的最古老也是最重要的抽象概念之一。操作系統的其他所有內容都是圍繞進程的概念展開的…

面對職業誘惑,我們如何作出理性的選擇?

版權聲明&#xff1a;原創作品&#xff0c;允許轉載&#xff0c;轉載時請務必以超鏈接形式標明文章原始出版、作者信息和本聲明。否則將追究法律責任。本文地址&#xff1a;http://blog.csdn.net/jobchanceleo/archive/2007/07/08/1682484.aspx 分享一個發生在我們身邊的案例&a…

xamarin怎么調用java的_XamarinSQLite教程在Xamarin.Android項目中使用數據庫

XamarinSQLite教程在Xamarin.Android項目中使用數據庫在Xamarin.Android項目中使用預設數據庫的具體操作步驟如下&#xff1a;(1)創建一個Xamarin.Android項目&#xff0c;如AndroidSQLiteDemo。(2)在AndroidSQLiteDemo項目的Resources文件夾下創建一個Raw文件夾。(3)將上一節中…

Selector的一些state使用

(一)Selector的基本狀態android:state_selected 控件選中狀態&#xff0c;可以為true或falseandroid:state_focused 控件獲得焦點狀態&#xff0c;可以為true或falseandroid:state_pressed 控件點擊狀態&#xff0c;可以為true或falseandroid:state_enabled 控件使能狀態&#…

服務框架及服務治理組件——業界調研

聲明&#xff1a;主要內容來自公司內部 對業界的調研,不一定恰當、準確、實時。 表格文字較多&#xff0c;APP閱讀體驗較差 團隊服務相關組件\方案通信框架監控負載均衡\路由是否開源騰訊完全自研&#xff1b;BG內部自治&#xff0c;每個BG有自己相應的解決方案&#xff0c;單獨…

在操作系統重啟后恢復應用程序的工作狀態

Windows 10 創意者更新之后&#xff0c;默認開啟了重啟后恢復應用程序狀態的功能。這是自 Vista 以來就提供的功能——Restart Manager。 應用程序實現這一功能只需要調用 RegisterApplicationRestart 即可。傳入兩個參數&#xff1a; 重啟后使用的命令行參數&#xff08;例如當…

裁員感悟

好員工&#xff0c;別以為裁員與你無關(上) 版權聲明&#xff1a;原創作品&#xff0c;允許轉載&#xff0c;轉載時請務必以超鏈接形式標明文章原始出版、作者信息和本聲明。否則將追究法律責任。本文地址&#xff1a;http://blog.csdn.net/jobchanceleo/archive/2007/05/26/…

php傳中文給Java_完美解決PHP中文亂碼(轉) - - JavaEye技術網站

PHP中文亂碼一般是字符集問題&#xff0c;編碼主要有下面幾個問題。一&#xff0e;首先是PHP網頁的編碼1.文件本身的編碼與網頁的編碼應匹配a.如果欲使用gb2312編碼&#xff0c;那么php要輸出頭&#xff1a;header(“Content-Type: text/html; charsetgb2312")&#xff0c…

CharSequence類

CharSequence是char類型的一個可讀序列&#xff0c;它本身是一個接口&#xff0c;CharBuffer、String、StringBuffer、StringBuilder這個四個類實現了這個接口。此接口對于不同種類的char序列提供統一的只讀訪問以下是這個函數的API 它只定義了四個方法 /*** This interface re…

程序員考核的五大死因

程序員考核的五大死因&#xff08;上&#xff09; 程序員作為企業開發力量的最核心資產&#xff0c;無疑得到公司從上至下的一致關注。開發是個智力密集型產業&#xff0c;程序開發的特點是&#xff0c;付出相同時間的情況下&#xff0c;兩個開發者之間的產能會相差十幾甚至幾…

java編寫螺旋矩陣講解_Java如何實現螺旋矩陣 Java實現螺旋矩陣代碼實例

本篇文章小編給大家分享一下Java實現螺旋矩陣代碼實例&#xff0c;小編覺得挺不錯的&#xff0c;現在分享給大家供大家參考&#xff0c;有需要的小伙伴們可以來看看。給定一個包含 m x n 個元素的矩陣(m 行, n 列)&#xff0c;請按照順時針螺旋順序&#xff0c;返回矩陣中的所有…

Vue Axios的配置 (高仿餓了么)

export default {name: "app",components: {"v-header": header},data() {return {seller: {}};},created() {let _this this; // 讓this始終代表最初this指向的對象this.axios.get(../data.json).then(function(res) {_this.seller res.data.sellercons…

PagerAdapter學習

前言: ViewGroup可以實現很多功能&#xff0c;如簡單的頁面導航和頁面滑動等等。谷歌公司為我們提供ViewGroup的API。谷歌公司推薦我們把ViewGroup和Fragment一起使,如果一起使用的話&#xff0c;應該使用FragmentPagerAdapter和FragmentStatePagerAdapter來進行適配處理&#…

arXiv網站

arXiv 原先是由物理學家保羅金斯巴格在1991年建立的網站&#xff0c; 我們會將預稿上傳到arvix作為預收錄&#xff0c;因此這就是個可以證明論文原創性&#xff08;上傳時間戳&#xff09;的文檔收錄網站。轉載于:https://www.cnblogs.com/AntonioSu/p/8387324.html