01揹包的變形問題 揹包恰好裝滿

2021-09-27 00:20:26 字數 3770 閱讀 4559

在看本文之前建議先看一下我之前發過的01揹包詳解。

在前面講到的01揹包問題中,現在我們把條件改為「求當揹包恰好裝滿時候取得的最大價值」。這樣的問題其實本質上和原始的01揹包問題區別不大,我們只需要做出一點小小的調整。

需要指出的是該問題其實可分為兩個問題。

(1)揹包能否恰好裝滿?

(2)如果能恰好裝滿,恰好裝滿時候的最大價值是多少?

為了便於理解,這裡給出乙個用陣列實現的01揹包**(空間複雜度未優化),和之前用vector容器實現的原理是一樣的,

//01揹包--不要求正好裝滿

#include

using

namespace std;

#define max(n1,n2) n1>n2?n1:n2

intmain()

;//狀態轉移矩陣,這裡將f陣列全部初始化為0

for(

int i =

1; i <= n; i++

)for

(int i =

1; i <= n; i++)}

cout << f[v]

<< endl;

//輸出結果

}return0;

}

在普通的01揹包中,由於不要求恰好裝滿,我們採用的初始化方式為

int f[

1000]=

;//狀態轉移矩陣,這裡將f陣列全部初始化為0

這個初始化的方法意思是:對任意的乙個狀態,把當前的價值初始化為0,代表揹包為空時所包含的物體價值為0。即認為不管揹包當前容量,揹包是空的那麼價值就是0,,沒裝滿揹包的狀態都是有效的狀態,我們稱之為有效狀態

當要求恰好裝滿時,我們就不可以這麼做了,因為我們認為揹包沒恰好裝滿的話,當前揹包的狀態無效,即無效狀態只有恰好裝滿時候才是有效狀態

我們不妨定義當揹包的狀態為無效狀態時,f[i]的值是負無窮,這樣我們就可以從狀態轉移矩陣中區分出有效狀態和無效狀態了。

先不關心如何計算f,有了這個定義,對於恰好裝滿的01揹包問題,我們只需要判斷f[v]中的值是否是負無窮,就知道揹包能否裝滿了。當f[v]中的值不是負無窮,我們接下來需要設計狀態轉移方程,使f[v]就是揹包恰好裝滿時候的最大價值。

首先觀察上面的**,轉移方程為

f[j]

=max

(f[j]

, f[j - v[i]

]+ w[i]

);

這段**時經過時間複雜度優化後的**,在優化前是

for

(int i =

1; i <= n; i++

)else

//如果揹包裝不下當前物體

}}

通過前面01揹包基本原理的講解,我們知道,這兩段**是等效的。

f[i][j] 的值是由 f[i - 1][j],f[i - 1][j - w[i]] 來推導出來的,也就是 f[i][j] 的取值只與前面的狀態有關係。換句話說,所有的狀態都是從以前的狀態來推導出來的。再換句話說,所有的有效狀態是從之前的有效狀態和無效狀態推導出來的!!!

接下來是重點!!!

接下來是重點!!!

接下來是重點!!!

如果無效狀態無法推出有效狀態,我們就不必關心無效狀態(揹包沒裝滿)時候,揹包內物體的總價值。

因為揹包恰好裝滿時候我們只關心有效狀態,而無效狀態又無法推出有效狀態。

也就可以把所有無效狀態時揹包的總價值都設定為負無窮。

我們來證明下無效狀態無法推出有效狀態

對於下面的狀態轉移方程

f[i]

[j]=

max(f[i -1]

[j], f[i -1]

[j - w[i]

]+ v[i]

);

(1)如果f[i - 1][j]和 f[i - 1][j - w[i]] 都是有效狀態,那麼f[i][j]是由有效狀態推出的。

(2)如果f[i - 1][j]是無效狀態, f[i - 1][j - w[i]] 是有效狀態:

此時f[i][j]揹包容量為j,f[i - 1][j - w[i]] 揹包容量為j - w[i]且是恰好裝滿的。從f[i - 1][j - w[i]] 變換到f[i][j]相當於向乙個已經裝了總體積為j - w[i]物體的揹包中放入乙個體積為w[i]的物體,新的容量為j與當前揹包容量j相同!!!

剛才我們說了無效狀態可以看作負無窮,於是 f[i - 1][j - w[i]] + v[i]作為有效狀態一定大於f[i - 1][j],所以可以得到新的有效狀態f[i][j]。

所以此時f[i][j]是有效狀態且是由有效狀態f[i - 1][j - w[i]]推出的。

(3)如果f[i - 1][j]是有效狀態, f[i - 1][j - w[i]] 是無效狀態:

f[i - 1][j - w[i]] 是無效狀態說明當前的揹包內裝的物體體積小於j - w[i],再放入乙個體積為w[i]的物體後容量小於j,也就是說f[i - 1][j - w[i]] + v[i]也是無效狀態。

此時因為f[i - 1][j]是有效狀態,所以一定大於無效狀態f[i - 1][j - w[i]] + v[i],則f[i][j]是乙個由效狀態f[i - 1][j]推導出的有效狀態。

所以此時f[i][j]是有效狀態且是由有效狀態f[i - 1][j]推出的。

綜上,只有有效狀態可以推出有效狀態。

由於c++中最小的負整型為16進製制數 0x80000000,我們無法做到真正的負無窮。於是為了在計算無效狀態時候依然採用同樣的狀態轉移方程,我們需要用點小手段,即

#define inf 0x80000000
for

(int j = v; j >= v[i]

; j--

)

如果算出來的是無效狀態,強制賦值為0x80000000。

這樣就相當於模擬了負無窮加上乙個有限的正整數的計算過程,即負無窮加上任意有限正數還是負無窮。

綜上,我們發現,恰好裝滿與不是恰好裝滿只是在初始化過程上有差別而已。只需要在初始化時候把所有的無效狀態初始化為負無窮就可以了。但是,f[0]需要初始化為0,因為揹包容量為0時,裝的物體體積為0是有效狀態!

//01揹包--恰好裝滿

#include

using

namespace std;

#define max(n1,n2) n1>n2?n1:n2

#define inf 0x80000000

intmain()

//f[0]是有效狀態

f[0]

=0;//輸入每個物體的體積和價值

for(

int i =

1; i <= n; i++

)//動態規劃過程

for(

int i =

1; i <= n; i++)}

//判斷能否恰好裝滿揹包

if(f[v]

>0)

else

}return0;

}

01揹包變式 要求恰好裝滿的01揹包

ktv裡面有n首歌曲你可以選擇,每首歌曲的時長都給出了.對於每首歌曲,你最多只能唱1遍.現在給你乙個時間限制t t 10 9 問你在最多t 1秒的時間內可以唱多少首歌曲num 且最長唱歌時間是多少time time必須 t 1 最終輸出num 1 和 time 678 即可.注意 你需要優先讓歌曲數...

01揹包問題變形

一 問題 二 解題思路 三 c 下面是我自己理解寫的,沒有根據標準答案的,那答案在講啥?一直沒法ac,不過還是可以解決問題的。n件物品按單位重量價值降序排序,然後回溯法裝,右結點必要時剪枝,剛好湊成重量為m的若干件物品才能得到乙個解。include define max 50 using names...

NYOJ 311 完全揹包 恰好裝滿完全揹包

時間限制 3000 ms 記憶體限制 65535 kb 難度 4 描述 直接說題意,完全揹包定義 有n種物品和乙個容量為v的揹包,每種物品都有無限件可用。第i種物品的體積是c,價值是w。求解將哪些物品裝入揹包可使這些物品的體積總和不超過揹包容量,且價值總和最大。本題要求是揹包 恰好裝滿揹包時,求出最...