开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 12 天,点击查看活动详情
今天学习了简单和中等的多重背包模型。
多重背包 I
题目描述
有 种物品和一个容量是 的背包。
第 种物品最多有 件,每件体积是 ,价值是 。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,,用空格隔开,分别表示物品种数和背包容积。
接下来有 行,每行三个整数 ,用空格隔开,分别表示第 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10
题目分析
由于多重背包问题对每件物品有数量的限制,所以我们可以将体积为 的物品具有 件分开,即每个物品作为一个单独的个体,这样我们就可以转化为总个数为 的01背包问题。
Accept代码 O(nvs)
#include <iostream>
using namespace std;
const int N = 110;
int n, m;
int v[N], w[N], s[N];
int f[N];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++) scanf("%d%d%d", &v[i], &w[i], &s[i]);
for (int i = 1; i <= n; i ++)
for (int j = m; j >= 1; j --)
for (int k = 0; v[i] * k <= j && k <= s[i]; k ++)
f[j] = max(f[j], f[j - v[i] * k] + w[i] * k);
cout << f[m];
return 0;
}
多重背包问题 II
数据范围
题目分析
本题题干与上一题一致,唯一的区别为扩大了数据的范围,若仍使用上题解法则会超时。
这里采取一种二进制优化的方案。
在朴素做法中,我们对于数量为 的物品采用从 到 依次枚举数量的方式。
在优化中,我们将总数为 的物品以 , 其中 件数重新划分,可以证明让新划分的物品重新组合可以还原为原物品范围件数。
题目复杂度由 优化为
Accept代码 O(nvlogs)
#include <bits/stdc++.h>
using namespace std;
const int N = 20010, M = 2010;
int v[N], w[N], f[M];
int cnt;
int main()
{
int n, m;
cin >> n >> m;
for (int i = 0; i < n; i ++)
{
int a, b, c;
cin >> a >> b >> c;
int k = 1;
while (c >= k)
{
v[cnt] = k * a;
w[cnt] = k * b;
c -= k;
k *= 2;
cnt ++;
}
if (c) v[cnt] = c * a, w[cnt ++] = c * b;
}
n = cnt;
for (int i = 0; i < n; i ++)
for (int j = m; j >= v[i]; j --)
f[j] = max(f[j], f[j - v[i]] + w[i]);
cout << f[m];
return 0;
}