【题目描述】
一个旅行者有一个最多能装 M公斤的背包,现在有 n件物品,它们的重量分别是W1,W2,...,Wn,它们的价值分别为C1,C2,...,Cn,求旅行者能获得最大总价值。
【输入】
第一行:两个整数,M(背包容量,M<=200)和N(物品数量,N<=30);
第2..N+1行:每行二个整数Wi,Ci,表示每个物品的重量和价值。
【输出】
仅一行,一个数,表示最大总价值。
【输入样例】
10 4
2 1
3 3
4 5
7 9
【输出样例】
12
【过程分析】
分析动态规划过程。输入样例:
- 背包容量
M=10
- 物品数量
N=4
- 每件物品的重量和价值如下:
- 物品1:重量
2
,价值1
- 物品2:重量
3
,价值3
- 物品3:重量
4
,价值5
- 物品4:重量
7
,价值9
- 物品1:重量
构建一个动态规划表dp
,其行表示考虑到的物品数量(从0开始,即不考虑任何物品的情况),列表示背包的容量(从0到M=10)。
dp | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
0物品 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1物品 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
2物品 | 0 | 0 | 1 | 3 | 3 | 4 | 4 | 4 | 4 | 4 | 4 |
3物品 | 0 | 0 | 1 | 3 | 5 | 5 | 6 | 8 | 8 | 9 | 9 |
4物品 | 0 | 0 | 1 | 3 | 5 | 5 | 6 | 9 | 9 | 10 | 12 |
分析过程:
- 0物品:不考虑任何物品时,任何容量的背包价值都是0。
- 1物品:只考虑第1件物品时,容量为2及以上的背包可以装入该物品,价值为1。
- 2物品:考虑到第2件物品时,可以看到当背包容量达到3时,可以装下第2件物品,价值增加到3。如果背包容量达到5或以上,可以装下第1和第2件物品,总价值为4。
- 3物品:加入第3件物品后,对于容量为4的背包,装入第3件物品的价值(5)比前两件物品的组合(1+3=4)价值更高,所以选择装入第3件物品。容量为7及以上时,可以装入第2件和第3件物品,价值为8。
- 4物品:最后,加入第4件物品。对于容量为7的背包,只装入第4件物品的价值(9)是最优选择。当背包容量为10时,可以装入第2件和第4件物品,总价值达到最大,为12。
最终,对于容量为10的背包,最大总价值为12。这通过考虑每件物品是否装入背包,并基于前面物品的选择结果来动态更新价值,以达到最优解。
【解题思路】
0-1背包问题,通过动态规划的方法来解决。动态规划是解决这类优化问题的一种有效手段,特别是在处理有关于重量和价值的问题时。
解题思路
1. 确定状态和状态转移方程
-
状态:定义
dp[i][j]
表示在前i
件物品中,能够装入容量为j
的背包的最大价值。这里i
可以取0
到N
(包含N
),j
可以取0
到M
(包含M
)。 -
状态转移方程:
- 如果不放第
i
件物品,那么dp[i][j] = dp[i-1][j]
; - 如果放第
i
件物品(前提是可以放下,即j >= w[i]
),那么dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + c[i])
,其中w[i]
和c[i]
分别是第i
件物品的重量和价值。
- 如果不放第
2. 初始化
- 初始化
dp[0][j] = 0
,因为没有物品时,任何容量的背包其价值都为0。
3. 遍历顺序
- 由于
dp[i][j]
的值依赖于上一行的值(即dp[i-1][...]
),因此物品的遍历是从1到N,背包容量的遍历是从0到M。
4. 计算结果
- 最终的结果就是
dp[N][M]
,表示在前N
件物品中,能装入容量为M
的背包的最大价值。
【实现步骤】
- 输入背包容量
M
和物品数量N
。 - 输入每件物品的重量
W
和价值C
。 - 使用一个二维数组
dp[N+1][M+1]
来保存状态。 - 初始化
dp[0][...] = 0
。 - 遍历物品和背包容量,根据状态转移方程更新
dp
数组。 - 输出
dp[N][M]
。
通过这种方法,可以有效地解决0-1背包问题,获得最大总价值。动态规划的优势在于它避免了重复计算,通过保存中间结果来提高效率。
【代码实现】
#include <iostream>
using namespace std;
const int MAX_N = 30; // 最大物品数量
const int MAX_M = 200; // 最大背包容量
int W[MAX_N + 1]; // 物品重量数组
int C[MAX_N + 1]; // 物品价值数组
int dp[MAX_N + 1][MAX_M + 1]; // 动态规划数组
int main() {
int M, N; // M为背包容量,N为物品数量
cin >> M >> N;
for (int i = 1; i <= N; ++i) {
cin >> W[i] >> C[i];
}
// 初始化dp数组
for (int i = 0; i <= N; ++i) {
for (int j = 0; j <= M; ++j) {
dp[i][j] = 0;
}
}
// 动态规划求解
for (int i = 1; i <= N; ++i) {
for (int j = 0; j <= M; ++j) {
if (j < W[i]) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - W[i]] + C[i]);
}
}
}
cout << dp[N][M] << endl;
return 0;
}
【代码解释】
实现使用了两个一维数组W
和C
来存储每件物品的重量和价值,另外使用了一个二维数组dp
来存储动态规划中的状态。dp[i][j]
表示在前i
件物品中,对于容量为j
的背包所能达到的最大价值。
代码首先读取背包的容量M
和物品的数量N
,然后读入每个物品的重量和价值。接下来,通过双重循环遍历所有物品和所有可能的背包容量,根据0-1背包问题的状态转移方程更新dp
数组。最终,dp[N][M]
即为所求的最大总价值,输出该值。