1268:【例9.12】完全背包问题
时间限制: 1000 ms 内存限制: 65536 KB
提交数:42708 通过数: 22902
【题目描述】
设有n种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为M,今从n种物品中选取若干件(同一种物品可以多次选取),使其重量的和小于等于M,而价值的和为最大。
【输入】
第一行:两个整数,M(背包容量,M≤200)和N(物品数量,N≤30);
第2..N+1行:每行二个整数Wi,Ci,表示每个物品的重量和价值。
【输出】
仅一行,一个数,表示最大总价值。
【输入样例】
10 4
2 1
3 3
4 5
7 9
【输出样例】
max=12
【解题思路】
这个问题是一个典型的完全背包问题,与0-1背包问题的主要区别在于每种物品可以选择无限次,直到背包装不下为止。解决这个问题的思路基于动态规划,关键在于正确设置状态转移方程,并对状态进行适当的遍历。
解题思路
1. 定义状态
定义一个动态规划数组dp[i]
,表示背包容量为i
时的最大价值。数组的大小为背包的最大容量M+1
。
2. 状态转移方程
对于每个物品,我们可以选择放入或者不放入背包,但由于物品的数量是无限的,所以如果选择放入,可以继续考虑放入该物品。因此,状态转移方程为:
dp[i]=max(dp[i],dp[i−W[j]]+C[j])
其中i
表示当前背包的容量,W[j]
和C[j]
分别表示第j
个物品的重量和价值。
3. 初始化
初始化dp[0]
为0,表示当背包容量为0时的最大价值为0。其余元素初始化为0或者一个很小的数值(取决于是否允许背包容量为负)。
4. 遍历顺序
由于物品的数量是无限的,遍历顺序变得十分关键。首先遍历物品,对于每种物品,再遍历背包的容量。由于我们考虑的是将物品放入背包,所以背包容量的遍历应该从小到大,这样可以确保在考虑放入当前物品时,依赖的状态已经被计算过了。
5. 计算最大价值
遍历完成后,dp[M]
即为背包容量为M
时的最大价值。
【过程分析】
假设我们有一个背包的最大容量为10,以及4种物品,每种物品的重量和价值分别如下:
- 物品1: 重量2,价值1
- 物品2: 重量3,价值3
- 物品3: 重量4,价值5
- 物品4: 重量7,价值9
我们的目标是找出在不超过背包最大容量的前提下,能获得的最大总价值。使用动态规划解决完全背包问题时,我们维护一个数组dp[i]
,其中dp[i]
表示背包容量为i
时的最大价值。下面是动态规划过程的逐步分析:
容量\物品 | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 1 | 1 | 1 | 1 |
3 | 0 | 1 | 3 | 3 | 3 |
4 | 0 | 2 | 3 | 5 | 5 |
5 | 0 | 2 | 4 | 6 | 6 |
6 | 0 | 3 | 6 | 6 | 6 |
7 | 0 | 3 | 6 | 8 | 9 |
8 | 0 | 4 | 7 | 10 | 10 |
9 | 0 | 4 | 8 | 11 | 12 |
10 | 0 | 5 | 9 | 12 | 12 |
分析过程:
- 初始化:当容量为0时,不管选择哪种物品,总价值都为0。
- 物品1更新:选择物品1时,每增加2个单位容量,价值增加1。例如,容量为2时,最大价值为1;容量为4时,最大价值为2,以此类推。
- 物品2更新:引入物品2后,容量为3的背包最大价值更新为3,容量为6的背包最大价值更新为6(可以选择两个物品2,总价值6),容量大于3时,开始出现根据不同选择策略更新的价值。
- 物品3更新:引入物品3后,容量为4的背包最大价值更新为5(选择一个物品3),对于更大容量的背包,根据放入物品3与之前的最优解进行比较,选择最大价值。
- 物品4更新:引入物品4后,容量为7的背包最大价值更新为9(仅放入一个物品4),随着容量增加,通过组合不同物品,找出最大价值。
最终结果:当背包容量为10时,最大总价值为12,这是通过组合物品2(重量3,价值3)和物品3(重量4,价值5)两次获得的(3+3+4=10,3+5+5=12)。这个结果通过动态规划的方式得到,确保了在每一步容量增加时,都能找到到目前为止可能获得的最大价值。
【实现步骤】
- 输入背包的最大容量
M
和物品的数量N
。 - 输入每件物品的重量和价值。
- 初始化动态规划数组
dp
。 - 遍历每件物品,对于每件物品,从小到大遍历所有可能的背包容量,更新
dp[i]
。 - 输出
dp[M]
作为最大总价值。
【实现代码】
#include <iostream>
using namespace std;
const int MAX_M = 200; // 最大背包容量
const int MAX_N = 30; // 最大物品数量
int W[MAX_N]; // 物品重量
int C[MAX_N]; // 物品价值
int dp[MAX_M + 1]; // 动态规划数组,记录每个容量下的最大价值
int main() {
int M, N; // M为背包容量,N为物品数量
cin >> M >> N;
for (int i = 0; i < N; ++i) {
cin >> W[i] >> C[i];
}
// 初始化dp数组
for (int i = 0; i <= M; ++i) {
dp[i] = 0;
}
// 动态规划求解
for (int i = 0; i < N; ++i) { // 遍历所有物品
for (int j = W[i]; j <= M; ++j) { // 从物品重量开始遍历所有背包容量
// 如果当前容量可以放下物品i,则考虑放入物品i
dp[j] = max(dp[j], dp[j - W[i]] + C[i]);
}
}
cout << "max="<<dp[M] << endl; // 输出最大价值
return 0;
}