多重揹包問題
該題含有三種揹包問題:
1、在本組中,至少拿一件物品的 0 1 揹包問題
2、在本組中,至多拿一件物品的 0 1 揹包問題
3、在本組中的 0 1 揹包問題
1、對於第一種:
由於至少拿一件物品,故初始化 爲 負無窮。因爲該 第 i 組,在各種體積下的合法狀態都是未知的,因爲不可以不拿,不能初始化爲 ,而求的是最大值,故初始化爲 負無窮。
由於求的是所有組合起來的最大價值,所以狀態肯定與上一組的狀態有關。
以下是關於 至少取一件 的三種轉移情況:
-
由於每組至少取一件,所以我可以是該組第一次取物品,那麼就與上一組的狀態有關。
所以有: -
如果這是該組取得第2.3.4…件,即不是第一件的話,那麼狀態由“該組取的第一件”轉化來。
所以有: -
在該組中,不取這件物品,那麼由於我一定要在本組中至少取一件,則狀態由本組轉移而來。所以有:
2、對於第二種:
時,代表前 組,體積爲 的情況。由於取至多一件,故可以不取,則初始化爲 。
而現在這個 的意義變化了,它應該是從上一組複製下來的,因爲我們需要的是進行本組的 揹包的初始狀態。而由於本應初始化爲 而非 無窮待定態,所以在本組的基礎上的初始狀態應該是上一組的末狀態。故一開始需要有
狀態由上一層轉移:
- 可以不取,故在上一層的基礎上不取本組的該物品,則有:
- 在本組中取這個物品,由於只能取一件,故從上一層轉移:
3、第三種爲普通 揹包問題,不再贅述。
代碼如下:
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int dp[108][108];
int n, t;
int m, s;
struct Bood
{
int w;
int val;
}A[10008];
int main()
{
while (~scanf("%d%d", &n, &t))
{
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++) {
scanf("%d%d", &m, &s);
for (int j = 1; j <= m; j++) {
scanf("%d%d", &A[j].w, &A[j].val);
}
if (s == 0) {
memset(dp[i], -0x3f, sizeof(dp[i]));
for (int j = 1; j <= m; j++) {
for (int k = t; k >= A[j].w; k--) {
dp[i][k] = max(dp[i - 1][k - A[j].w] + A[j].val, max(dp[i][k], dp[i][k - A[j].w] + A[j].val));
}
}
}
else if (s == 1) {
for (int j = 0; j <= t; j++) {
dp[i][j] = dp[i - 1][j];
}
for (int j = 1; j <= m; j++) {
for (int k = t; k >= A[j].w; k--) {
dp[i][k] = max(dp[i][k], dp[i - 1][k - A[j].w] + A[j].val);
}
}
}
else {
for (int j = 0; j <= t; j++) {
dp[i][j] = dp[i - 1][j];
}
for (int j = 1; j <= m; j++) {
for (int k = t; k >= A[j].w; k--) {
dp[i][k] = max(dp[i][k], dp[i][k - A[j].w] + A[j].val);
}
}
}
}
printf("%d\n", max(-1,dp[n][t]));
}
}