Skip to content

Commit c6b3743

Browse files
committed
DP
多重背包 多维度费用的背包 背包的解题思路 背包变型题
1 parent a6464c3 commit c6b3743

14 files changed

+282
-49
lines changed

src/DP/背包/划分数组为和相等的两部分.java

Lines changed: 0 additions & 35 deletions
This file was deleted.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package DP.背包;
2+
3+
/**
4+
* Created by 周杰伦 on 2018/4/8.
5+
*/
6+
public class 多维费用的背包 {
7+
//是01背包的延伸,费用变成了2维
8+
//接下来的注释是背包问题的一般解法
9+
public static int knapsack2(int W1,int W2, int N, int[][] weights, int[] values) {
10+
int [][]dp = new int[W1 + 1][W2 + 1];
11+
for (int i = 1; i <= N; i++) {
12+
int w1 = weights[i - 1][0],w2 = weights[i - 1][1], v = values[i - 1];
13+
for (int j = W1; j >= w1;j --) {
14+
for (int k = W2;j >= w2;k -- )
15+
dp[j][k] = Math.max(dp[j][k], dp[j - w1][k - w2] + v);
16+
}
17+
}
18+
return dp[W1][W2];
19+
}
20+
}

src/DP/背包/多重背包.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88
public class 多重背包 {
99
//首先是正规解法,和完全背包类似。
10+
//多重背包限制了每个物品的数量,介于01和完全背包之间。
1011
public static int knapsack(int W, int N, int[] weights, int[] values, int []nums) {
1112
int[][] dp = new int[N + 1][W + 1];
1213
//物品数为0时,取法初始化为0,代表不能装满,但是可以进行累加。因为不需要装满。

src/DP/分割整数构成字母字符串.java renamed to src/DP/背包/字符串/分割整数构成字母字符串.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package DP;
1+
package DP.背包.字符串;
22

33
/**
44
* Created by 周杰伦 on 2018/4/3.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package DP.背包.字符串;
2+
3+
/**
4+
* Created by 周杰伦 on 2018/4/8.
5+
*/
6+
public class 字符01构成最多的字符串 {
7+
public static void main(String[] args) {
8+
String []a = {"10", "0001", "111001", "1", "0"};
9+
System.out.println(findMaxForm(a, 5, 3));
10+
}
11+
public static int findMaxForm(String[] strs, int m, int n) {
12+
//代表i个0和j个1能够成最多的字符串个数
13+
//首先写出矩阵
14+
int [][]dp = new int[m + 1][n + 1];
15+
dp[0][0] = 0;
16+
//然后根据条件写一个商品的for循环
17+
for (String str : strs) {
18+
int needZero = 0;
19+
int needOne = 0;
20+
//再根据条件写费用的循环。
21+
//可能是二维费用,则有两个循环。多重背包也类似
22+
//如果是完全背包,则用顺序,01背包用逆序。
23+
24+
//该步相当于计算放入第i件物品所需的费用。
25+
//因为是二维的,所以类比到01背包,在背包中就是重量和体积
26+
for (int i = 0;i < str.length();i ++) {
27+
if (str.charAt(i) == '0')needZero ++;
28+
else needOne ++;
29+
}
30+
for (int j = m;j >= needZero;j --) {
31+
for (int k = n;k >= needOne;k --) {
32+
dp[j][k] = Math.max(dp[j - needZero][k - needOne] + 1, dp[j][k]);
33+
}
34+
}
35+
}
36+
return dp[m][n];
37+
}
38+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package DP.背包.字符串;
2+
3+
import java.util.ArrayList;
4+
import java.util.Arrays;
5+
import java.util.List;
6+
7+
/**
8+
* Created by 周杰伦 on 2018/4/6.
9+
*/
10+
public class 字符串按单词列表分割 {
11+
public static void main(String[] args) {
12+
String s = "leetcode";
13+
List<String> wordDict = new ArrayList<>();
14+
wordDict.add("leet");
15+
wordDict.add("code");
16+
System.out.println(wordBreak(s,wordDict));
17+
}
18+
//该题与完全背包还是有差异的。解法比较巧妙,最好记着
19+
public static boolean wordBreak(String s, List<String> wordDict) {
20+
//dp[i] represents if s.substring(0, i) is wordbreakable.
21+
boolean []dp = new boolean[s.length() + 1];
22+
Arrays.fill(dp, false);
23+
dp[0] = true;
24+
for (int i = 1;i <= s.length();i ++) {
25+
for (int j = 0;j < i;j ++) {
26+
if (dp[j] && wordDict.contains(s.substring(j,i))) {
27+
dp[i] = true;
28+
break;
29+
}
30+
}
31+
}
32+
return dp[s.length()];
33+
}
34+
//这是一个完全背包问题,和 0-1 背包不同的是,完全背包中物品可以使用多次。
35+
// 在这一题当中,词典中的单词可以被使用多次。
36+
37+
//0-1 背包和完全背包在实现上的不同之处是,0-1 背包对物品的迭代是在最外层,
38+
// 而完全背包对物品的迭代是在最里层。
39+
public boolean wordBreak2(String s, List<String> wordDict) {
40+
int n = s.length();
41+
boolean[] dp = new boolean[n + 1];
42+
dp[0] = true;
43+
for (int i = 1; i <= n; i++) {
44+
for (String word : wordDict) { // 每个单词可以使用多次
45+
int len = word.length();
46+
if (len <= i && word.equals(s.substring(i - len, i))) {
47+
dp[i] = dp[i] || dp[i - len];
48+
}
49+
}
50+
}
51+
return dp[n];
52+
}
53+
}

src/DP/背包/字符串按单词列表分割.java

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/DP/背包/背包01不用装满.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public static void main(String[] args) {
1717
public static int knapsack(int W, int N, int[] weights, int[] values) {
1818

1919
int[][] dp = new int[N + 1][W + 1];
20+
//不用装满就设为0,需要装满的话,求最大值时设为负无穷,求最小值时设为正无穷。
2021
//物品数为0时,取法初始化为0,代表不能装满,但是可以进行累加。因为不需要装满。
2122
Arrays.fill(dp[0], 0);
2223
for (int i = 1; i <= N; i++) {

src/DP/背包/背包01需要装满.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public static int knapsack(int W, int N, int[] weights, int[] values) {
3535
// 优化一维
3636
public static int knapsack2(int W, int N, int[] weights, int[] values) {
3737
int []dp = new int[W + 1];
38-
//前0个物品永远无法装满1-W重量的背包。置为负无穷,dp累加时只考虑前面已经装满的清空
38+
//前0个物品永远无法装满1-W重量的背包。置为负无穷,dp累加时只考虑前面已经装满的情况
3939
Arrays.fill(dp, Integer.MIN_VALUE);
4040
//前0个物品刚好可以装满容量为0的背包,此时价值为0,以此为初始值进行累加。
4141
dp[0]= 0;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package DP.背包.背包扩展;
2+
3+
import java.sql.Array;
4+
import java.util.Arrays;
5+
6+
/**
7+
* Created by 周杰伦 on 2018/4/6.
8+
*/
9+
public class 划分数组为和相等的两部分 {
10+
public static void main(String[] args) {
11+
int []a = {1, 5, 11, 5};
12+
System.out.println(canPartition2(a));
13+
}
14+
//优化版
15+
//本题的背包重量就是和的一半。
16+
//每个数都是一个物品的费用。
17+
//于是得dp[i] = dp[i - num[i]] || dp[i]
18+
//由于本题是判断能否加到一个和,所以只判断true和false
19+
//如果是凑成的数量就是 + 1,如果是价值就是加v[i]
20+
public static boolean canPartition2(int[] nums) {
21+
int sum = 0;
22+
for (int i : nums) {
23+
sum += i;
24+
}
25+
if (sum % 2 != 0)return false;
26+
sum = sum/2;
27+
//代表前i个数选出的和可以累加为j。但是i可以选择拿或不拿。
28+
boolean []dp = new boolean[sum + 1];
29+
Arrays.fill(dp, false);
30+
dp[0] = true;
31+
32+
for (int i = 0;i < nums.length;i ++) {
33+
for (int j = sum;j >= nums[i];j --) {
34+
dp[j] = dp[j] || dp[j - nums[i]];
35+
}
36+
}
37+
return dp[sum];
38+
}
39+
40+
41+
public boolean canPartition(int[] nums) {
42+
int sum = 0;
43+
for (int i : nums) {
44+
sum += i;
45+
}
46+
if (sum % 2 != 0)return false;
47+
sum = sum/2;
48+
//代表前i个数选出的和可以累加为j。但是i可以选择拿或不拿。
49+
boolean [][]dp = new boolean[nums.length + 1][sum + 1];
50+
for (int j = 0;j < nums.length + 1;j ++) {
51+
Arrays.fill(dp[j], false);
52+
}
53+
dp[0][0] = true;
54+
for (int i = 1;i <= nums.length;i ++) {
55+
for (int j = 1;j <= sum;j ++) {
56+
if (j >= nums[i - 1]) {
57+
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]];
58+
}else {
59+
dp[i][j] = dp[i - 1][j];
60+
}
61+
}
62+
}
63+
return dp[nums.length][sum];
64+
}
65+
66+
}

0 commit comments

Comments
 (0)