背包问题
背包问题是一种组合优化的NP问题。可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中,背包的空间有限,但我们需要最大化背包内所装物品的价值。 背包问题通常出现在资源分配中,决策者必须分别从一组不可分割的项目或任务中进行选择,而这些项目又有时间或预算的限制。
01背包问题
题意概要: 有 n 个物品和一个容量为 W 的背包,每个物品有重量 w_i 和价值v_i 两种属性,要求选若干物品放入背包使背包中物品的总价值最大,且背包中物品的总重量不超过背包的容量。
由于每个物体只有两种可能的状态(放与不放),对应二进制中的 0 和 1,这类问题便被称为「0-1 背包问题」。
暴力法解决
时间复杂度为O(2^n),n表示物品数量,不行。
动态规划
定义状态:定义子问题
dp[i][j]:表示将 i 件物品装进限重为 j 的背包可以获得的最大价值;
状态转移方程:描述子问题之间的联系,需分类讨论:
基于子问题的最优解(前i-1个物品),对于第 i 个物品,有两种可能的状态:
- 不放进背包:背包容量不变,价值也不变化,及继承之前子问题的结果,dp[i][j] = dp[i-1][j];
- 放进背包:背包容量会减少 w[i],价值发生增加 v[i],dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
放第 i 个物品,此时背包容量要减去w[i],dp[i - 1][j - w[i]]为剩余容量 j - w[i] 限制下的最大值(前i-1个物品最大值),然后加上第 i 个物品的价值 v[i]
状态转移方程:
代码
public static int solution(int[] w, int[] v, int m) {
int n = w.length; // 物品个数
int[][] dp = new int[n][m + 1];
for (int i = 0; i <= m; i++) {
if (i >= w[0]) { // 初始化dp
dp[0][i] = v[0];
}
}
for (int i = 1; i < n; i++) { // 遍历物品
for (int j = 1; j <= m; j++) { // 遍历重量
if (w[i] > j) { // 不放物品(放不下)
dp[i][j] = dp[i - 1][j];
} else { // 放物品
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
}
}
}
// 最大价值
return dp[n - 1][m];
}