01背包问题
基础背包
1 2 3 4 5 6 7
| int[] dp = new int[weight+1]; for(int i=1;i<=n;i++){ for(int j=weight;j>=nums[i-1];j--){ dp[j] = Math.max(dp[j-nums[i-1]]+nums[i-1],dp[j]); } } return dp[weight];
|
目标和
leetcode 494
思路:见代码注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| public static int findTargetSumWays4(int[] nums, int target) { int sum = 0; for (int n : nums) { sum += n; } if (sum < target || ((target & 1) ^ (sum & 1)) == 1) { return 0; } return subsets(nums, (target + sum) >> 1); }
public static int subsets(int[] nums, int t) { if (t < 0) { return 0; } int[] dp = new int[t + 1]; dp[0] = 1; for (int num : nums) { for (int j = t; j >= num; j--) { dp[j] += dp[j - num]; } } return dp[t]; }
|
最后一块石头的重量
leetcode 1049
思路:转化为找出一个数组中最接近和的一半的子序列和。答案就是sum - 2 * 子序列和,找最接近和的一半的子序列和就是01背包问题。背包容量为一半,然后找出价值最高的子序列和。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public int lastStoneWeightII(int[] stones) { int sum = 0; for (int stone : stones) { sum+=stone; } int near = findLSW(stones,sum/2); return sum - near - near; }
private int findLSW(int[] stones, int i) { int[] dp = new int[i+1]; for (int stone : stones) { for(int j=i;j>=stone;j--){ dp[j] = Math.max(dp[j],dp[j-stone]+stone); } } return dp[i]; }
|
分组背包
模板题
同一个组的物品只能选一个,求最大价值。
思路:还是分为要跟不要。但是要的情况需要讨论要一组里的哪一个。所以dp[i][j] 中的i表示物品组,j表示背包容量。开始时可以按组号排序,一组一组处理即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| public static int compute1() { int teams = 1; for (int i = 2; i <= n; i++) { if (arr[i - 1][2] != arr[i][2]) { teams++; } } int[][] dp = new int[teams + 1][m + 1]; for (int start = 1, end = 2, i = 1; start <= n; i++) { while (end <= n && arr[end][2] == arr[start][2]) { end++; } for (int j = 0; j <= m; j++) { dp[i][j] = dp[i - 1][j]; for (int k = start; k < end; k++) { if (j - arr[k][0] >= 0) { dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - arr[k][0]] + arr[k][1]); } } } start = end++; } return dp[teams][m]; }
public static int compute2() { Arrays.fill(dp, 0, m + 1, 0); for (int start = 1, end = 2; start <= n;) { while (end <= n && arr[end][2] == arr[start][2]) { end++; } for (int j = m; j >= 0; j--) { for (int k = start; k < end; k++) { if (j - arr[k][0] >= 0) { dp[j] = Math.max(dp[j], arr[k][1] + dp[j - arr[k][0]]); } } } start = end++; } return dp[m]; }
|
从栈中取出k个硬币的最大面值和
leetcode 2218
思路:以一个栈里面的硬币为分组。一组里面的元素就是拿一个硬币,两个硬币,三个硬币…得到的面值。一组我也只能拿一个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
|
public static int maxValueOfCoins1(List<List<Integer>> piles, int m) { int n = piles.size(); int[][] dp = new int[n + 1][m + 1]; for (int i = 1; i <= n; i++) { List<Integer> team = piles.get(i - 1); int t = Math.min(team.size(), m); int[] preSum = new int[t + 1]; for (int j = 0, sum = 0; j < t; j++) { sum += team.get(j); preSum[j + 1] = sum; } for (int j = 0; j <= m; j++) { dp[i][j] = dp[i - 1][j]; for (int k = 1; k <= Math.min(t, j); k++) { dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k] + preSum[k]); } } } return dp[n][m]; }
public static int maxValueOfCoins2(List<List<Integer>> piles, int m) { int[] dp = new int[m + 1]; for (List<Integer> team : piles) { int t = Math.min(team.size(), m); int[] preSum = new int[t + 1]; for (int j = 0, sum = 0; j < t; j++) { sum += team.get(j); preSum[j + 1] = sum; } for (int j = m; j > 0; j--) { for (int k = 1; k <= Math.min(t, j); k++) { dp[j] = Math.max(dp[j], dp[j - k] + preSum[k]); } } } return dp[m]; }
|
完全背包
模板题
一个物品可以重复取,求最大价值。
dp[i][j] = Max(dp[i-1][j],dp[i][j-w[i]]+v[i]) //拿了之后i也不减一,就可以拿多次。空间压缩需要正着遍历。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public static long compute1() { int[][] dp = new int[m + 1][t + 1]; for (int i = 1; i <= m; i++) { for (int j = 0; j <= t; j++) { dp[i][j] = dp[i - 1][j]; if (j - cost[i] >= 0) { dp[i][j] = Math.max(dp[i][j], dp[i][j - cost[i]] + val[i]); } } } return dp[m][t]; }
public static long compute2() { Arrays.fill(dp, 1, t + 1, 0); for (int i = 1; i <= m; i++) { for (int j = cost[i]; j <= t; j++) { dp[j] = Math.max(dp[j], dp[j - cost[i]] + val[i]); } } return dp[t]; }
|