揹包問題不僅僅是一個簡單的算法問題,它本質上表明瞭一大類問題,這類問題其實是01線性規劃問題,其約束條件和目標函數以下:
html
自從dd_engi在2007年推出《揹包問題九講》以後,揹包問題的主要精髓基本已道盡。本文沒有嘗試對揹包問題的本質進行擴展或深刻挖掘,而只是從有限的理解(這裏指對《揹包問題九講》的理解)出發,幫助讀者更快地學習《揹包問題九講》中的提到的各類揹包問題的主要算法思想,並經過實例解釋了相應的算法,同時給出了幾個揹包問題的經典應用。算法
dd_engi在《揹包問題九講》中主要提到四種揹包問題,分別爲:01揹包問題,徹底揹包問題,多重揹包問題,二維費用揹包問題。本節總結了這幾種揹包問題,並給出了其典型的應用以幫助讀者理解這幾種問題的本質。數組
2.1 01揹包問題函數
(1)問題描述學習
有N件物品和一個容量爲V的揹包。第i件物品的費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可以使價值總和最大。優化
(2)狀態轉移方程spa
其中,f(i,v) 表示從前i件物品選擇若干物品裝到容量爲v的揹包中產生的最大價值。當v=0時,f(i,v)初始化爲0,表示題目不要求揹包必定恰好裝滿,而f(i,v)=inf/-inf(正無窮或負無窮)表示題目要求揹包必定要恰好裝滿。下面幾種揹包相似,之後再也不贅述。htm
(3)僞代碼get
從轉移方程上能夠看出,前i個物品的最優解只依賴於前i-1個物品最優解,而與前i-2,i-3,…各物品最優無直接關係,能夠利用這個特色優化存儲空間,即只申請一個一維數組便可,算法時間複雜度(O(VN))爲:博客
注意v的遍歷順序!!!
下面幾種揹包用到相似優化,之後再也不贅述。
(4)舉例
V=10,N=3,c[]={3,4,5}, w={4,5,6}
(1)揹包不必定裝滿
計算順序是:從右往左,自上而下:
(2)揹包恰好裝滿
計算順序是:從右往左,自上而下。注意初始值,其中-inf表示負無窮
(5)經典題型
[1] 你有一堆石頭質量分別爲W1,W2,W3…WN.(W<=100000,N <30)如今須要你將石頭合併爲兩堆,使兩堆質量的差爲最小。
[2] 給一個整數的集合,要把它分紅兩個集合,要兩個集合的數的和最接近
[3] 有一個箱子容量爲V(正整數,0≤V≤20000),同時有n個物品(0小於n≤30),每一個物品有一個體積(正整數)。要求從n個物品中,任取若干個裝入箱內,使箱子的剩餘空間爲最小。
2.2 徹底揹包問題
(1)問題描述
有N種物品和一個容量爲V的揹包,每種物品都有無限件可用。第i種物品的費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可以使這些物品的費用總和不超過揹包容量,且價值總和最大。
(2)狀態轉移方程
或者:
(3)僞代碼
注意v的遍歷順序!!!
注意,時間複雜度仍爲:O(VN).
(4)舉例
V=10,N=3,c[]={3,4,5}, w={4,5,6}
(1)揹包不必定裝滿
計算順序是:從左往右,自上而下:
(2)揹包恰好裝滿
計算順序是:從左往右,自上而下。注意初始值,其中-inf表示負無窮
(5)經典題型
[1] 找零錢問題:有n種面額的硬幣,每種硬幣無限多,至少用多少枚硬幣表示給定的面值M?
2.3 多重揹包問題
(1)問題描述
有N種物品和一個容量爲V的揹包。第i種物品最多有n[i]件可用,每件費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可以使這些物品的費用總和不超過揹包容量,且價值總和最大。
(2)狀態轉移方程
(3)解題思想
用如下方法轉化爲普通01揹包問題:將第i種物品分紅若干件物品,其中每件物品有一個係數,這件物品的費用和價值均是原來的費用和價值乘以這個係數。使這些係數分別爲 1,2,4,…,2^(k-1),n[i]-2^k+1,且k是知足n[i]-2^k+1>0的最大整數。例如,若是n[i]爲13,就將這種 物品分紅係數分別爲1,2,4,6的四件物品。這種方法能保證對於0..n[i]間的每個整數,都可以用若干個係數的和表示。這個很容易證實,證實過程當中用到如下定理:任何一個整數n都可以表示成:n=a0*2^0+a1*2^1+…+ak*2^k,其中ak=0或者1(實際上就是n的二進制分解),
定理:一個正整數n能夠被分解成1,2,4,…,2^(k-1),n-2^k+1(k是知足n-2^k+1>0的最大整數)的形式,且1~n以內的全部整數都可以惟一表示成1,2,4,…,2^(k-1),n-2^k+1中某幾個數的和的形式。
該定理的證實以下:
(1) 數列1,2,4,…,2^(k-1),n-2^k+1中全部元素的和爲n,因此若干元素的和的範圍爲:[1, n];
(2)若是正整數t<= 2^k – 1,則t必定能用1,2,4,…,2^(k-1)中某幾個數的和表示,這個很容易證實:咱們把t的二進制表示寫出來,很明顯,t能夠表示成n=a0*2^0+a1*2^1+…+ak*2^(k-1),其中ak=0或者1,表示t的第ak位二進制數爲0或者1.
(3)若是t>=2^k,設s=n-2^k+1,則t-s<=2^k-1,於是t-s能夠表示成1,2,4,…,2^(k-1)中某幾個數的和的形式,進而t能夠表示成1,2,4,…,2^(k-1),s中某幾個數的和(加數中必定含有s)的形式。
(證畢!)
該算法的時間複雜度爲:O(V*sum(logn[i])).
(4)經典題型
[1] 找零錢問題:有n種面額的硬幣,分別爲a[0], a[1],…, a[n-1],每種硬幣的個數爲b[0], b[1],…, b[n-1],至少用多少枚硬幣表示給定的面值M?
2.4 二維費用揹包
(1)問題描述
二維費用的揹包問題是指:對於每件物品,具備兩種不一樣的費用;選擇這件物品必須同時付出這兩種代價;對於每種代價都有一個可付出的最大值(揹包容量)。問 怎樣選擇物品能夠獲得最大的價值。設這兩種代價分別爲代價1和代價2,第i件物品所需的兩種代價分別爲a[i]和b[i]。兩種代價可付出的最大值(兩種 揹包容量)分別爲V和U。物品的價值爲w[i]。
(2)狀態轉移方程
(3)算法思想
採用同一維狀況相似的方法求解
(4)經典題型
有2n個整數,平均分紅兩組,每組n個數,使這兩組數的和最接近。
揹包問題實際上表明的是線性規劃問題,通常要考慮如下幾個因素已決定選取什麼樣的算法:
(1) 約束條件,有一個仍是兩個或者更多個,若是是一個,多是01揹包,徹底揹包或者多重揹包問題,若是有兩個約束條件,則多是二維揹包問題。
(2) 優化目標,求最大值,仍是最小值,仍是總數(只需將max換成sum)
(3) 每種物品的個數限制,若是每種物品只有一個,只是簡單的01揹包問題,若是個數無限制,則是徹底揹包問題,若是每種物品的個數有限制,則是多重揹包問題。
(4) 揹包是否要求恰好塞滿,注意不塞滿和塞滿兩種狀況下初始值不一樣。
dd_engi:《揹包問題九講》,available:http://www.oiers.cn/pack/Index.html