题目
题目链接
解题思路
- 这是一个多重背包问题,每种物品有指定的数量限制
- 可以将多重背包转化为0-1背包来解决:
- 对每种物品,最多可以选择其数量上限个
- 使用逆序遍历避免重复计算
- 状态定义:
- \(dp[j]\) 表示容量为 \(j\) 时能获得的最大价值
- 状态转移:
- \(dp[j] = \max(dp[j], dp[j-w[i]] + s[i])\)
代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, v;
cin >> n >> v;
vector<int> m(n); // 物品数量
vector<int> w(n); // 物品体积
vector<int> s(n); // 物品价值
vector<int> dp(v + 1, 0); // dp数组
// 读取每种物品的信息
for(int i = 0; i < n; i++) {
cin >> m[i] >> w[i] >> s[i];
}
// 多重背包
for(int i = 0; i < n; i++) { // 遍历物品
for(int k = 0; k < m[i]; k++) { // 遍历物品数量
for(int j = v; j >= w[i]; j--) {// 逆序遍历容量
dp[j] = max(dp[j], dp[j-w[i]] + s[i]);
}
}
}
cout << dp[v] << endl;
return 0;
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); // 物品种类
int v = sc.nextInt(); // 背包容量
int[] m = new int[n]; // 物品数量
int[] w = new int[n]; // 物品体积
int[] s = new int[n]; // 物品价值
int[] dp = new int[v + 1]; // dp数组
// 读取每种物品的信息
for(int i = 0; i < n; i++) {
m[i] = sc.nextInt();
w[i] = sc.nextInt();
s[i] = sc.nextInt();
}
// 多重背包
for(int i = 0; i < n; i++) {
for(int k = 0; k < m[i]; k++) {
for(int j = v; j >= w[i]; j--) {
dp[j] = Math.max(dp[j], dp[j-w[i]] + s[i]);
}
}
}
System.out.println(dp[v]);
}
}
def solve_knapsack(n, v, items):
# 初始化dp数组
dp = [0] * (v + 1)
# 处理每种物品
for i in range(n):
m, w, s = items[i] # 数量、体积、价值
# 对每个物品可用的数量进行处理
for k in range(m):
# 逆序遍历容量
for j in range(v, w-1, -1):
dp[j] = max(dp[j], dp[j-w] + s)
return dp[v]
# 读取输入
n, v = map(int, input().split())
items = []
for _ in range(n):
m, w, s = map(int, input().split())
items.append((m, w, s))
# 输出结果
print(solve_knapsack(n, v, items))
算法及复杂度
- 算法:动态规划(多重背包转化为0-1背包)
- 时间复杂度:\(\mathcal{O}(n \cdot v \cdot \sum m_i)\) - \(n\) 为物品种类,\(v\) 为背包容量,\(m_i\) 为每种物品的数量
- 空间复杂度:\(\mathcal{O}(v)\) - 需要一个容量大小的 \(dp\) 数组