01背包问题
题目
题目描述
有 N N N 件物品和一个容量是 V V V 的背包。每件物品只能使用一次。
第 i i i 件物品的体积是 v i v_i vi,价值是 w i w_i wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数, N , V N,V N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N N N 行,每行两个整数 v _ i , w _ i v\_i, w\_i v_i,w_i,用空格隔开,分别表示第 i i i 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0
<
N
,
V
≤
1000
0 \lt N, V \le 1000
0<N,V≤1000
0
<
v
i
,
w
i
≤
1000
0\lt v_i, w_i \le 1000
0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8
思路
我们用动态规划解决问题时需要经过如下几个步骤:
-
确定动态规划的状态表示,即 d p dp dp 数组所代表的含义。
-
根据第1步确定的状态表示来推想动态规划转移方程。
-
分析时间复杂度,如果复杂度不在我们允许的范围之内,尝试优化动态规划转移复杂度或重新定义状态表示。
对于这道01背包问题,我们执行以下步骤:
- d p [ i ] [ j ] dp[i][j] dp[i][j]表示在前 i i i 件物品里选出若干件,且物品体积和不超过 j j j 能取到物品的最大价值。若我们可以求解出 d p dp dp 数组,则 d p [ N ] [ V ] dp[N][V] dp[N][V] 就是我们要求的答案了。
- 考虑如何求得
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]。根据我们定义的状态,
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示在前
i
i
i 件物品中选取若干件体积和不超过
j
j
j 的最大价值,因此
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 与
d
p
[
i
−
1
]
[
]
dp[i - 1][\ ]
dp[i−1][ ] 的差别只在于第
i
i
i 件物品在
d
p
[
i
−
1
]
[
]
dp[i - 1][\ ]
dp[i−1][ ] 没被考虑,所以我们可以通过
d
p
[
i
−
1
]
[
]
dp[i - 1][\ ]
dp[i−1][ ] 推出
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]。
接下来分别考虑第 i i i 件物品选或不选。
如果我们选择第 i i i 件物品,那么从前 i − 1 i - 1 i−1 种物品中选出的体积就不能超过 j − v i j - v_i j−vi ,所能得到的最大价值为 d p [ i − 1 ] [ j − v i ] + w [ i ] dp[i - 1][j - v_i] + w[i] dp[i−1][j−vi]+w[i];
若不选择第 i i i 件物品,则延续之前的最大价值,最大价值为 d p [ i − 1 [ [ j ] dp[i - 1[[j] dp[i−1[[j]。
取这两种情况的最大值;因此,我们的状态转移方程为:
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − v i ] + w i ) dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v_i] + w_i) dp[i][j]=max(dp[i−1][j],dp[i−1][j−vi]+wi)
注意:我们需要保证目前枚举到的 j j j 是大于等于当前物品体积的,否则说明现在的状态背包根本就装不下这个物品,只能延续之前的最大价值。 - 计算时间复杂度:我们不难发现,求解单个状态的复杂度仅为 O ( 1 ) O(1) O(1);所以总时间复杂度则为状态数 O ( N V ) O(NV) O(NV)
代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1010, M = 1010;
int n, V;
int v[N], w[N];
int dp[N][M];
int main() {
cin >> n >> V;
for (int i = 1; i <= n; ++ i ) cin >> v[i] >> w[i];
for (int i = 1; i <= n; ++ i )
for (int j = 0; j <= V; ++ j )
if (j >= v[i]) dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i]);
else dp[i][j] = dp[i - 1][j];
cout << dp[n][V] << '\n';
return 0;
}