什么是背包问题
背包问题是:一类组合优化的问题,它可以描述为:给定一组物品,每种物品都有自己的重量和价值,在限定的总重量内,如何选择这些物品放入背包,以使得背包内物品的总价值最大。背包问题根据其特定的约束条件和规则,可以分为几种不同的类型,其中最著名的包括0/1背包问题、完全背包问题和多重背包问题。
1. 0/1背包问题
在0/1背包问题中,每种物品只有一件,可以选择放入或不放入背包,因此称为0/1背包问题。目标是在不超过背包最大重量限制的前提下,最大化背包内物品的总价值。
例题P1048 [NOIP2005 普及组] 采药 https://www.luogu.com.cn/problem/P1616
分析:解决问题的关键是找到动态转移方程dp[i][j] max(dp[i - 1][j - w[i]] + val[i], dp[i - 1][j])
#include <stdio.h>
int w[105], val[105];
int dp[105][1005];
int max(int a, int b) {
return (a > b) ? a : b;
}
int main() {
int t, m;
scanf("%d%d", &t, &m); // 读入背包总容量和物品数量
for (int i = 1; i <= m; i++) {
scanf("%d%d", &w[i], &val[i]); // 读入每个物品的重量和价值
}
// 动态规划求解0/1背包问题
for (int i = 1; i <= m; i++) {
for (int j = t; j >= 0; j--) {
if (j >= w[i]) {
dp[i][j] = max(dp[i - 1][j - w[i]] + val[i], dp[i - 1][j]);
}
else {
dp[i][j] = dp[i - 1][j];
}
}
}
printf("%d", dp[m][t]); // 输出最大价值
return 0;
}
2. 完全背包问题
与0/1背包问题不同,完全背包问题允许每种物品有无限件可用。也就是说,只要背包的容量允许,你可以放入任意数量的某种物品。同样的目标是最大化背包内物品的总价值。
例题 P1616 疯狂的采药https://www.luogu.com.cn/problem/P1616
#include <stdio.h>
int w[105], val[105];
int dp[1005];
int max(int a, int b) {
return (a > b) ? a : b;
}
int main() {
int t, m;
scanf("%d%d", &t, &m); // 读入背包总容量和物品数量
for (int i = 1; i <= m; i++) {
scanf("%d%d", &w[i], &val[i]); // 读入每个物品的重量和价值
}
// 动态规划求解完全背包问题
for (int i = 1; i <= m; i++) {
for (int j = w[i]; j <= t; j++) {
// 对于完全背包问题,当考虑加入物品i时,应该从w[i]开始遍历到t
// 因为只有当背包容量至少为物品i的重量时,才有可能放入物品
dp[j] = max(dp[j], dp[j - w[i]] + val[i]);
}
}
printf("%d", dp[t]); // 输出最大价值
return 0;
}
3. 多重背包问题
多重背包问题是0/1背包问题和完全背包问题的一种泛化形式。在多重背包问题中,每种物品有限定的数量,不是只有一件,也不是无限件。目标依然是在背包容量限制下,最大化总价值。
背包问题的求解
背包问题可以通过动态规划、贪心算法等方法求解。动态规划是最常用的方法之一,特别适合解决这类优化问题。它通过将问题分解成更小的子问题,并找出子问题间的依赖关系,逐步构建最终解决方案。
动态规划求解背包问题的基本思想:
- 状态定义:定义一个状态表示函数,通常是一个二维数组或一维数组,用来记录达到某种状态时的最优解(最大价值)。
- 状态转移:找出状态之间的转移关系,即如何从一个或多个已知状态推导出新的状态。
- 初始化:确定初始状态,即在一开始时背包的状态。
- 计算顺序:确定状态计算的顺序,保证在计算当前状态时,所依赖的状态已经被计算过。
- 结果输出:根据定义的状态表示函数,输出最终的最优解。