9、01 背包问题
1、01 背包问题
public class Solution {
private static class Node {
public int index;
public boolean b; // w[index] 要不要?
public int w;
public int v;
public Node left; // w[index + 1] 要
public Node right; // w[index + 1] 不要
public Node() {
}
public Node(int index, boolean b, int w, int v) {
this.index = index;
this.b = b;
this.w = w;
this.v = v;
}
}
private static Node root;
/**
* w[i] 代表重量, v[i] 代表价值, C 代表背包容量
*/
public static int knapsack01(int[] w, int[] v, int C) {
if (w == null || w.length == 0 || v == null || v.length == 0) return 0;
if (w.length != v.length) return 0;
root = new Node();
Queue<Node> queue = new LinkedList<>();
queue.add(root);
for (int i = 0; i < w.length; i++) {
List<Node> level = new ArrayList<>(); // 每一层的节点
int size = queue.size();
for (int j = 0; j < size; j++) level.add(queue.remove());
for (Node node : level) {
node.left = new Node(i, true, node.w + w[i], node.v + v[i]);
node.right = new Node(i, false, node.w, node.v);
queue.add(node.left);
queue.add(node.right);
}
}
List<Node> allNode = new ArrayList<>();
perOrder(root, allNode);
int res = -1;
for (Node node : allNode) {
if (node.w <= C) res = Math.max(res, node.v);
}
return res;
}
private static void perOrder(Node node, List<Node> list) {
if (node == null) return;
list.add(node);
perOrder(node.left, list);
perOrder(node.right, list);
}
}
2、贪心算法
3、递归
3.1、图示
3.2、实现
/**
* w[i] 代表重量, v[i] 代表价值, C 代表背包容量
*/
public static int knapsack01(int[] w, int[] v, int C) {
if (w == null || w.length == 0 || v == null || v.length == 0) return 0;
if (w.length != v.length) return 0;
return dp(w, v, w.length - 1, C);
}
/**
* 用 w[0 ... index] 的物品, 填充最大容量为 c 的背包的最大价值
*/
private static int dp(int[] w, int[] v, int index, int c) {
if (index < 0 || c <= 0) return 0;
// w[index] 的价值为 v[index], w[index] 要不要
int res = dp(w, v, index - 1, c); // 不要
if (c >= w[index]) {
res = Math.max(res, v[index] + dp(w, v, index - 1, c - w[index])); // 要
}
return res;
}
4、记忆化搜索
public class Solution {
private static int[][] memo;
/**
* w[i] 代表重量, v[i] 代表价值, C 代表背包容量
*/
public static int knapsack01(int[] w, int[] v, int C) {
if (w == null || w.length == 0 || v == null || v.length == 0) return 0;
if (w.length != v.length) return 0;
memo = new int[w.length][C + 1];
for (int[] arr : memo) Arrays.fill(arr, -1);
return dp(w, v, w.length - 1, C);
}
/**
* 用 w[0 ... index] 的物品, 填充最大容量为 c 的背包的最大价值
*/
private static int dp(int[] w, int[] v, int index, int c) {
if (index < 0 || c <= 0) return 0;
if (memo[index][c] != -1) return memo[index][c];
// w[index] 的价值为 v[index], w[index] 要不要
int res = dp(w, v, index - 1, c); // 不要
if (c >= w[index]) {
res = Math.max(res, v[index] + dp(w, v, index - 1, c - w[index])); // 要
}
memo[index][c] = res;
return res;
}
}
5、动态规划
5.1、图示
5.2、实现
public class Solution {
/**
* w[i] 代表重量, v[i] 代表价值, C 代表背包容量
*/
public static int knapsack01(int[] w, int[] v, int C) {
if (w == null || w.length == 0 || v == null || v.length == 0) return 0;
if (w.length != v.length) return 0;
// memo[i][c] = 用 w[0 ... i] 的物品, 填充最大容量为 c 的背包的最大价值
int[][] memo = new int[w.length][C + 1];
for (int[] arr : memo) Arrays.fill(arr, -1);
for (int c = 0; c <= C; c++) {
// memo[0][c] = 用 w[0] 的物品, 填充最大容量为 c 的背包的最大价值
memo[0][c] = (c >= w[0] ? v[0] : 0);
}
for (int i = 1; i < memo.length; i++) {
for (int c = 0; c <= C; c++) {
// memo[i][c] = 用 w[0 ... i] 的物品, 填充最大容量为 c 的背包的最大价值
// w[i] 的价值为 v[i], w[i] 要不要
int res = memo[i - 1][c]; // 不要
if (c >= w[i]) {
res = Math.max(res, v[i] + memo[i - 1][c - w[i]]); // 要
}
memo[i][c] = res;
}
}
return memo[w.length - 1][C];
}
}
6、01 背包问题的优化
6.1、空间优化 1
public class Solution {
/**
* w[i] 代表重量, v[i] 代表价值, C 代表背包容量
*/
public static int knapsack01(int[] w, int[] v, int C) {
if (w == null || w.length == 0 || v == null || v.length == 0) return 0;
if (w.length != v.length) return 0;
// 用 w[0 ... i] 的物品, 填充最大容量为 c 的背包的最大价值 = memo[i % 2][c]
int[][] memo = new int[2][C + 1];
for (int[] arr : memo) Arrays.fill(arr, -1);
for (int c = 0; c <= C; c++) {
// 用 w[0] 的物品, 填充最大容量为 c 的背包的最大价值 = memo[0][c]
memo[0][c] = (c >= w[0] ? v[0] : 0);
}
for (int i = 1; i <= w.length - 1; i++) {
for (int c = 0; c <= C; c++) {
// 用 w[0 ... i] 的物品, 填充最大容量为 c 的背包的最大价值 = memo[i % 2][c]
// w[i] 的价值为 v[i], w[i] 要不要
int res = memo[(i - 1) % 2][c]; // 不要
if (c >= w[i]) {
res = Math.max(res, v[i] + memo[(i - 1) % 2][c - w[i]]); // 要
}
memo[i % 2][c] = res;
}
}
return memo[(w.length - 1) % 2][C];
}
}
6.2、空间优化 2
public class Solution {
/**
* w[i] 代表重量, v[i] 代表价值, C 代表背包容量
*/
public static int knapsack01(int[] w, int[] v, int C) {
if (w == null || w.length == 0 || v == null || v.length == 0) return 0;
if (w.length != v.length) return 0;
// memo[c] = 用 w[0 ... i] 的物品, 填充最大容量为 c 的背包的最大价值
int[] memo = new int[C + 1];
Arrays.fill(memo, -1);
for (int c = 0; c <= C; c++) {
// memo[c] = 用 w[0] 的物品, 填充最大容量为 c 的背包的最大价值
memo[c] = (c >= w[0] ? v[0] : 0);
}
for (int i = 1; i <= w.length - 1; i++) {
for (int c = C; c >= w[i]; c--) {
// memo[c] = 用 w[0 ... i] 的物品, 填充最大容量为 c 的背包的最大价值
// w[i] 的价值为 v[i], w[i] 要不要
memo[c] = Math.max(memo[c], v[i] + memo[c - w[i]]);
if (i == w.length - 1 && c == C) return memo[c]; // 在这里或许可以提前返回
}
}
return memo[C];
}
}
7、01 背包问题的变种
8、面试中的 01 背包问题
9.1、图示
9.2、递归
public static boolean canPartition(int[] nums) {
int sum = 0;
for (int num : nums) sum += num;
if (sum % 2 != 0) return false;
int c = sum / 2;
return dp(nums, nums.length - 1, c);
}
/**
* 使用 nums[0 ... index] 中的数字, 能否完全填满一个容量为 c 的背包
*/
private static boolean dp(int[] nums, int index, int c) {
if(c == 0) return true;
if (index < 0 || c < 0) return false;
// nums[index] 要不要
boolean res = false;
res = res || dp(nums, index - 1, c); // 不要
res = res || dp(nums, index - 1, c - nums[index]); // 要
return res;
}
9.3、记忆化搜索
public class CanPartition2 {
private static int[][] memo;
public static boolean canPartition(int[] nums) {
int sum = 0;
for (int num : nums) sum += num;
if (sum % 2 != 0) return false;
int c = sum / 2;
// memo[i][c] = 使用 nums[0 ... i] 中的数字, 能否完全填满一个容量为 c 的背包
memo = new int[nums.length][c + 1]; // 0 -> false, 1 -> true
for (int[] arr : memo) Arrays.fill(arr, -1);
return dp(nums, nums.length - 1, c);
}
/**
* 使用 nums[0 ... index] 中的数字, 能否完全填满一个容量为 c 的背包
*/
private static boolean dp(int[] nums, int index, int c) {
if (c == 0) return true;
if (index < 0 || c < 0) return false;
if (memo[index][c] != -1) return memo[index][c] == 1;
// nums[index] 要不要
boolean res = false;
res = res || dp(nums, index - 1, c); // 不要
res = res || dp(nums, index - 1, c - nums[index]); // 要
memo[index][c] = res ? 1 : 0;
return res;
}
}
9.4、动态规划
public static boolean canPartition(int[] nums) {
int sum = 0;
for (int num : nums) sum += num;
if (sum % 2 != 0) return false;
int C = sum / 2;
// memo[c] = 用 nums[0 ... i] 中的数字, 能否完全填满一个容量为 c 的背包
boolean[] memo = new boolean[C + 1];
for (int c = 0; c <= C; c++) {
// memo[c] = 用 nums[0] 中的数字, 能否完全填满一个容量为 c 的背包
memo[c] = nums[0] == c;
}
for (int i = 1; i <= nums.length - 1; i++) {
for (int c = C; c >= nums[i]; c--) {
memo[c] = memo[c] || memo[c - nums[i]];
if (i == nums.length - 1 && c == C) return memo[c]; // 在这里或许可以提前返回
}
}
return memo[C];
}
本文来自博客园,作者:lidongdongdong~,转载请注明原文链接:https://www.cnblogs.com/lidong422339/p/17435730.html