题目
解题思路
-
问题分析:
- 第一问:求背包能装下的最大价值(不要求装满)
- 第二问:求背包恰好装满时的最大价值(要求装满)
-
状态定义:
- d p 1 [ j ] dp1[j] dp1[j]:容量为 j j j 时能装下的最大价值(不要求装满)
- d p 2 [ j ] dp2[j] dp2[j]:容量为 j j j 时恰好装满的最大价值(要求装满)
-
初始化:
- d p 1 dp1 dp1 全部初始化为 0 0 0(表示空背包,价值为 0 0 0)
- d p 2 dp2 dp2 除了 d p 2 [ 0 ] = 0 dp2[0]=0 dp2[0]=0 外,其他都初始化为负无穷(表示还未找到恰好装满的方案)
-
状态转移:
对每个物品
i
i
i:
对每个容量
j
j
j 从
V
V
V 到
v
i
v_i
vi:
d p 1 [ j ] = max ( d p 1 [ j ] , d p 1 [ j − v i ] + w i ) dp1[j] = \max(dp1[j], dp1[j-v_i] + w_i) dp1[j]=max(dp1[j],dp1[j−vi]+wi) // 不要求装满
d p 2 [ j ] = max ( d p 2 [ j ] , d p 2 [ j − v i ] + w i ) dp2[j] = \max(dp2[j], dp2[j-v_i] + w_i) dp2[j]=max(dp2[j],dp2[j−vi]+wi) // 要求装满
-
结果获取:
- 第一问:直接输出 d p 1 [ V ] dp1[V] dp1[V]
- 第二问:如果 d p 2 [ V ] dp2[V] dp2[V] 为负无穷,输出 0 0 0;否则输出 d p 2 [ V ] dp2[V] dp2[V]
-
优化方案:
- 使用滚动数组(一维dp)节省空间
- 从后向前遍历避免重复使用物品
- 使用
long long类型避免整数溢出
代码
#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, -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()
算法及复杂度
- 算法:动态规划(01背包)
- 时间复杂度: O ( n V ) \mathcal{O}(nV) O(nV), n n n 是物品数量, V V V 是背包容量
- 空间复杂度: O ( V ) \mathcal{O}(V) O(V),使用滚动数组优化
1021

被折叠的 条评论
为什么被折叠?



