解题思路
这是一个完全背包问题的变体,需要解决两个子问题:
-
问题分析:
- 第一问:求背包能装下的最大价值(不要求装满)
- 第二问:求背包恰好装满时的最大价值(要求装满)
- 每种物品可以使用无限次
-
状态定义:
- :容量为j时能装下的最大价值(不要求装满)
- :容量为j时恰好装满的最大价值(要求装满)
-
与01背包的区别:
- 完全背包正序遍历容量(因为物品可重复使用)
- 01背包逆序遍历容量(避免物品重复使用)
-
状态转移:
- 对每个物品 :
- 对每个容量 从 到 :
- // 不要求装满
- // 要求装满
- 对每个容量 从 到 :
- 对每个物品 :
代码
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main() {
int n, V;
cin >> n >> V;
// 不要求装满的dp数组
vector<long long> dp1(V + 1, 0);
// 恰好装满的dp数组
vector<long long> dp2(V + 1, LLONG_MIN);
dp2[0] = 0;
for(int i = 0; i < n; i++) {
int v, w;
cin >> v >> w;
// 完全背包正序遍历
for(int j = v; j <= V; j++) {
if(dp1[j-v] != LLONG_MIN)
dp1[j] = max(dp1[j], dp1[j-v] + w);
if(dp2[j-v] != LLONG_MIN)
dp2[j] = max(dp2[j], dp2[j-v] + w);
}
}
cout << dp1[V] << endl;
cout << (dp2[V] == LLONG_MIN ? 0 : dp2[V]) << endl;
return 0;
}
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int V = sc.nextInt();
// 不要求装满的dp数组
long[] dp1 = new long[V + 1];
// 恰好装满的dp数组
long[] dp2 = new long[V + 1];
Arrays.fill(dp2, Long.MIN_VALUE);
dp2[0] = 0;
for(int i = 0; i < n; i++) {
int v = sc.nextInt();
int w = sc.nextInt();
// 完全背包正序遍历
for(int j = v; j <= V; j++) {
if(dp1[j-v] != Long.MIN_VALUE)
dp1[j] = Math.max(dp1[j], dp1[j-v] + w);
if(dp2[j-v] != Long.MIN_VALUE)
dp2[j] = Math.max(dp2[j], dp2[j-v] + w);
}
}
System.out.println(dp1[V]);
System.out.println(dp2[V] == Long.MIN_VALUE ? 0 : dp2[V]);
sc.close();
}
}
def solve_knapsack():
n, V = map(int, input().split())
# 不要求装满的dp数组
dp1 = [0] * (V + 1)
# 恰好装满的dp数组
dp2 = [-float('inf')] * (V + 1)
dp2[0] = 0
for _ in range(n):
v, w = map(int, input().split())
# 完全背包正序遍历
for j in range(v, V + 1):
if dp1[j-v] != -float('inf'):
dp1[j] = max(dp1[j], dp1[j-v] + w)
if dp2[j-v] != -float('inf'):
dp2[j] = max(dp2[j], dp2[j-v] + w)
print(dp1[V])
print(dp2[V] if dp2[V] != -float('inf') else 0)
if __name__ == "__main__":
solve_knapsack()
算法及复杂度
- 算法:动态规划(完全背包)
- 时间复杂度:, 是物品种类数, 是背包容量
- 空间复杂度:,使用一维 数组