初識0 1依賴揹包

2021-10-03 07:58:23 字數 3219 閱讀 6577

hdu3449

題意:

給定物品的組數n和一開始擁有的錢數m,後面n行輸入代表n組物品,若要購買該組物品中的乙個或多個都需要先購買盒子,盒子無價值,每組物品中最開始兩個數bp和mi分別表示盒子的**和該組物品的種數,問用m元最多可獲得貨物的最**值是多少。

思路:

典型的有依賴的0-1揹包問題,根據dd大牛的揹包九講,基本思路為先將每一組物品看作乙個集合,對第i組物品進行一次0-1揹包的選取,得到dp[i][j]即花j元在前i組物品中可獲得的最大價值,後再對dp[i-1][j]和dp[i][j] 再取一次最優解,dp[i-1][j]表示用j元情況下不選第i組中的物品,dp[i][j]表示選第i組物品下的最優解,選與不選取最優解dp[i][j]即為在j元下的最優解。

實現的細節問題:

首先選某組物品之前一定要選箱子,每組對應的箱子有**而無價值,為保證在對每一組物品進行選取討論時首先得選箱子,所以對於錢數j小於還是大於第i組箱子**的情況分開初始化:當擁有的錢j小於第i組箱子**bp的情況下dp[i][j]初始化為-1(非法狀態);而當擁有的錢j大於第i組箱子**bp的情況下dp[i][j]初始化為dp[i-1][j-bp] ( 需要保證在能購買第 i 組的箱子的情況下即錢數要先扣除買箱子的錢bp後,dp[i]陣列繼承dp[i-1]陣列的最優解 )。

另外在對於第i組物品進行0-1揹包討論的狀態轉移方程中取第i組的第k件物品dp[i][k-w] + v時要注意保證dp[i][k-w]合法,一方面需要保證k>w(**的迴圈條件中已經保證,很基礎的0-1揹包迴圈),另一方面討論dp[i][k-w]時要保證是在合法情況下討論亦即買了**為w的物品後要有餘額購買第i組的箱子bp,**中表現為dp[i][k-w] != -1

hdu坑點吐槽:

需要多組樣例,這點題目中並未說明,而不加多組樣例會wa

#include

#include

#define ll long long

#define mem( f, x ) memset( f, x, sizeof( f ))

#define inf 0x3f3f3f3f

#define pii pair

using

namespace std;

const

int m =52;

const

int n =

100005

;int dp[m]

[n];

intmain()

for(

int j =

0; j <= m; j++

) dp[i]

[j]=

max( dp[i]

[j], dp[i-1]

[j]);}

printf

("%d\n"

, dp[n]

[m]);}

return0;

}

滾動陣列優化空間:

由上述**可以看出dp開了50*100005的空間但每一次i用到的只有dp[i]和dp[i-1]陣列,因此可以只開兩個陣列,乙個dp陣列儲存dp[i]陣列,另乙個f陣列儲存dp[i-1]陣列即可。另外需要注意的是當第i組結束後f陣列要將dp中的資料儲存下來作為下一輪的dp[i-1]使用,意為滾動。

#include

#include

#define ll long long

#define mem(f, x) memset( f, x, sizeof(f))

#define inf 0x3f3f3f3f

#define pii pair

using

namespace std;

const

int m =52;

const

int n =

100005

;int dp[n]

, f[n]

;int

main()

for(

int j =

0; j <= m; j++)}

printf

("%d\n"

, dp[m]);

}return0;

}

思路:將每個主件及其附件看作乙個集合(組),對問題進行劃分,對於第i組只有兩種情況:

(1). 不在第i組中取任何物品,這時直接繼承前i-1組的最優解即可。

(2). 取第i組中的某種組合,此時對第i組物品種的所有物品進行一次01揹包的討論即可,但是需要注意的是由於主件本身有一定的花費,所以對01揹包的第二重迴圈揹包的容量不能從全取,需要預留出主件的花費。因此揹包容量

for(j = m-bp…w)逆向迴圈,這裡可以看成在討論揹包容量j時預先扣除了主件大小pb,實際的總容量應是j = m … w + bp的逆迴圈,若以f陣列保留中間結果,則在取最終結果時dp[j]所對應的應該是f[j-bp],當下的dp[j]所對應的是前i-1組的最優解,f[j-bp]中儲存的是取第i組情況下的最優解,兩者再取一次最優解即可。

#include

#include

#define ll long long

#define inf 0x3f3f3f3f

#define mem( f, x ) memset( f, x, sizeof( f ) )

#define pii pair

#define fi first

#define se second

using

namespace std;

const

int m =

1e5+5;

const

int n =

1e5+5;

int m, n;

int dp[n]

, f[n]

;int

main()

for(

int j = bp; j <= m; j++

) dp[j]

=max

( dp[j]

, f[j-bp]);

}printf

("%d\n"

, dp[m]);

}return0;

}

有依賴的0 1揹包

考慮到每個主件最多只有兩個附件,因此我們可以通過轉化,把原問題轉化為01揹包問題來解決,在用01揹包之前我們需要對輸入資料進行處理,把每一種物品歸類,即 把每乙個主件和它的附件看作一類物品。處理好之後,我們就可以使用01揹包演算法了。在取某件物品時,我們只需要從以下四種方案中取最大的那種方案 只取主...

有依賴的01揹包問題

王強今天很開心,公司發給n元的年終獎。王強決定把年終獎用於購物,他把想買的物品分為兩類 主件與附件,附件是從屬於某個主件的,下表就是一些主件與附件的例子 主件附件 電腦印表機,掃瞄器 書櫃圖書 書桌檯燈,文具 工作椅無 如果要買歸類為附件的物品,必須先買該附件所屬的主件。每個主件可以有 0 個 1 ...

HDU1203(初識01揹包)

原題鏈結 01揹包是在m件物品取出若干件放在空間為w的揹包裡,每件物品的體積為w1,w2至wn,與之相對應的價值為p1,p2至pn。01揹包的約束條件是給定幾種物品,每種物品有且只有乙個,並且有權值和體積兩個屬性。因為每種物品只有乙個,對於每個物品只需要考慮選與不選兩種情況。hdu1203很明顯是一...