揹包問題

0-1揹包

基本思路

這是最基礎的揹包問題,特色是:每種物品僅有一件,能夠選擇放或不放。算法

用子問題定義狀態:即f[i][v]表示前i件物品恰放入一個容量爲v的揹包能夠得到的最大價值。則其狀態轉移方程即是:編程

f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}code

這個方程很是重要,基本上全部跟揹包相關的問題的方程都是由它衍生出來的。因此有必要將它詳細解釋一下:「將前i件物品放入容量爲v的揹包中」這個子問題,若只考慮第i件物品的策略(放或不放),那麼就能夠轉化爲一個只牽扯前i-1件物品的問題。若是不放第i件物品,那麼問題就轉化爲「前i-1件物品放入容量爲v的揹包中」,價值爲f[i-1][v];若是放第i件物品,那麼問題就轉化爲「前i-1件物品放入剩下的容量爲v-c[i]的揹包中」,此時能得到的最大價值就是f[i-1][v-c[i]]再加上經過放入第i件物品得到的價值w[i]。隊列

徹底揹包

基本思路

這個問題很是相似於01揹包問題,所不一樣的是每種物品有無限件。也就是從每種物品的角度考慮,與它相關的策略已並不是取或不取兩種,而是有取0件、取1件、取2件……等不少種。若是仍然按照解01揹包時的思路,令f[i][v]表示前i種物品恰放入一個容量爲v的揹包的最大權值。仍然能夠按照每種物品不一樣的策略寫出狀態轉移方程,像這樣:ip

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}class

這跟01揹包問題同樣有O(VN)個狀態須要求解,但求解每一個狀態的時間已經不是常數了,求解狀態f[i][v]的時間是O(v/c[i]),總的複雜度能夠認爲是O(V*Σ(V/c[i])),是比較大的。基礎

將01揹包問題的基本思路加以改進,獲得了這樣一個清晰的方法。這說明01揹包問題的方程的確是很重要,能夠推及其它類型的揹包問題。但咱們仍是試圖改進這個複雜度。循環

多重揹包

基本算法

這題目和徹底揹包問題很相似。基本的方程只需將徹底揹包問題的方程略微一改便可,由於對於第i種物品有n[i]+1種策略:取0件,取1件……取n[i]件。令f[i][v]表示前i種物品恰放入一個容量爲v的揹包的最大權值,則有狀態轉移方程:方法

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i]}im

複雜度是O(V*Σn[i])。

混合三種揹包

01揹包與徹底揹包的混合

考慮到在P01和P02中給出的僞代碼只有一處不一樣,故若是隻有兩類物品:一類物品只能取一次,另外一類物品能夠取無限次,那麼只需在對每一個物品應用轉移方程時,根據物品的類別選用順序或逆序的循環便可,複雜度是O(VN)。僞代碼以下:

for i=1..N
    if 第i件物品屬於01揹包
        for v=V..0
            f[v]=max{f[v],f[v-c[i]]+w[i]};
    else if 第i件物品屬於徹底揹包
        for v=0..V
            f[v]=max{f[v],f[v-c[i]]+w[i]};

再加上多重揹包

若是再加上有的物品最多能夠取有限次,那麼原則上也能夠給出O(VN)的解法:遇到多重揹包類型的物品用單調隊列解便可。但若是不考慮超過NOIP範圍的算法的話,用P03中將每一個這類物品分紅O(log n[i])個01揹包的物品的方法也已經很優了。

固然,更清晰的寫法是調用咱們前面給出的三個相關過程。

for i=1..N
    if 第i件物品屬於01揹包
        ZeroOnePack(c[i],w[i])
    else if 第i件物品屬於徹底揹包
        CompletePack(c[i],w[i])
    else if 第i件物品屬於多重揹包
        MultiplePack(c[i],w[i],n[i])

在最初寫出這三個過程的時候,可能徹底沒有想到它們會在這裏混合應用。我想這體現了編程中抽象的威力。若是你一直就是以這種「抽象出過程」的方式寫每一類揹包問題的,也很是清楚它們的實現中細微的不一樣,那麼在遇到混合三種揹包問題的題目時,必定能很快想到上面簡潔的解法,對嗎?

相關文章
相關標籤/搜索