Skip to content

Commit da839fe

Browse files
committed
DP
背包问题集锦 01背包 二维 一维 要求满 不要求满 完全背包 二维 一维 3次循环 2次循环 放满 不放满 多重背包 二维 一维 3循环 2循环利用01背包 最后一种是根据情况选择完全背包和01背包
1 parent f31d3c0 commit da839fe

6 files changed

+324
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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 boolean canPartition(int[] nums) {
11+
int sum = 0;
12+
for (int i : nums) {
13+
sum += i;
14+
}
15+
if (sum % 2 != 0)return false;
16+
sum = sum/2;
17+
//代表前i个数选出的和可以累加为j。但是i可以选择拿或不拿。
18+
boolean [][]dp = new boolean[nums.length + 1][sum + 1];
19+
for (int j = 0;j < nums.length + 1;j ++) {
20+
Arrays.fill(dp[j], false);
21+
}
22+
dp[0][0] = true;
23+
for (int i = 1;i <= nums.length;i ++) {
24+
for (int j = 1;j <= sum;j ++) {
25+
if (j >= nums[i - 1]) {
26+
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]];
27+
}else {
28+
dp[i][j] = dp[i - 1][j];
29+
}
30+
}
31+
}
32+
return dp[nums.length][sum];
33+
}
34+
35+
}

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

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package DP.背包;
2+
3+
import java.util.Arrays;
4+
5+
/**
6+
* Created by 周杰伦 on 2018/4/7.
7+
*/
8+
public class 多重背包 {
9+
//首先是正规解法,和完全背包类似。
10+
public static int knapsack(int W, int N, int[] weights, int[] values, int []nums) {
11+
int[][] dp = new int[N + 1][W + 1];
12+
//物品数为0时,取法初始化为0,代表不能装满,但是可以进行累加。因为不需要装满。
13+
Arrays.fill(dp[0], 0);
14+
for (int i = 1; i <= N; i++) {
15+
int w = weights[i - 1], v = values[i - 1];
16+
for (int j = w; j <= W; j++) {
17+
for (int k = 1;k <= nums[i];i ++) {
18+
if (j >= k * w) {
19+
// 那只是一种理解方法,其实正规的应该是这样的
20+
// dp[i][j] = max ( dp[i-1][j - k*weight[i]] +k*value[i] )0<=k<=nums[i]
21+
// (这个跟完全背包差点就一毛一样了啊喂|||- -)
22+
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - k * w] + k * v);
23+
} else {
24+
dp[i][j] = dp[i - 1][j];
25+
}
26+
}
27+
}
28+
}
29+
return dp[N][W];
30+
}
31+
32+
//根据01背包进行优化
33+
//首先这种可以把物品拆开,把相同的num[i]件物品 看成
34+
// 价值跟重量相同的num[i]件不同的物品,那么!!
35+
// 是不是就转化成了一个规模稍微大一点的01背包了。对不对!!对不对!!
36+
public static int knapsack2(int W, int N, int[] weights, int[] values, int []nums) {
37+
int []dp = new int[W + 1];
38+
for (int i = 1; i <= N; i++) {
39+
int w = weights[i - 1], v = values[i - 1];
40+
//其实就是把这类物品展开,调用num[i]次01背包代码
41+
for (int k = 1;k <= nums[i];k ++) {
42+
for (int j = W; j >= w; j--) {
43+
////正常的01背包代码
44+
dp[j] = Math.max(dp[j], dp[j - w] + v);
45+
}
46+
}
47+
}
48+
return dp[W];
49+
}
50+
51+
//最优化的多重背包。
52+
//第i件物品若满足总数*重量小于背包重量,即可用完全背包来解。
53+
//若不满足,则按照1 2 4的二进制速度上升。复杂度为log2n
54+
int Multi_Pack(int wegihts[],int values[],int nums[],int N,int W)//多重背包
55+
{
56+
int []dp = new int[N + 1];
57+
for(int i=1; i<=N; i++)//遍历每种物品
58+
{
59+
if(nums[i]*wegihts[i] > W)
60+
Complete_Pack(wegihts[i],values[i],W,dp);
61+
//如果全装进去已经超了重量,相当于这个物品就是无限的
62+
//因为是取不光的。那么就用完全背包去套
63+
else
64+
{
65+
int k = 1;
66+
//取得光的话,去遍历每种取法
67+
//这里用到是二进制思想,降低了复杂度
68+
//为什么呢,因为他取的1,2,4,8...与余数个该物品,打包成一个大型的该物品
69+
//这样足够凑出了从0-k个该物品取法
70+
//把复杂度从k变成了logk
71+
//如k=11,则有1,2,4,4,足够凑出0-11个该物品的取法
72+
while(k < nums[i])
73+
{
74+
ZeroOne_Pack(k*wegihts[i],k*values[i],W,dp);
75+
nums[i] -= k;
76+
k <<= 1;
77+
}
78+
//如果这一次装k个的空间不够了,就把该价值剩下的物品装进去
79+
ZeroOne_Pack(nums[i]*wegihts[i],nums[i]*values[i],W,dp);
80+
}
81+
}
82+
return dp[W];
83+
}
84+
85+
void ZeroOne_Pack(int weight,int value,int W,int []dp)//吧01背包封装成函数
86+
{
87+
for(int i=W; i>=weight; i--)
88+
dp[i] = Math.max(dp[i],dp[i-weight] + value);
89+
}
90+
91+
void Complete_Pack(int weight,int value,int W,int []dp)//把完全背包封装成函数
92+
{
93+
for(int i=weight; i<=W; i++)
94+
dp[i] = Math.max(dp[i],dp[i-weight] + value);
95+
}
96+
97+
98+
99+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package DP.背包;
2+
3+
import java.util.List;
4+
5+
/**
6+
* Created by 周杰伦 on 2018/4/6.
7+
*/
8+
public class 字符串按单词列表分割 {
9+
public boolean wordBreak(String s, List<String> wordDict) {
10+
return true;
11+
}
12+
}

src/DP/背包/完全背包.java

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package DP.背包;
2+
3+
import java.util.Arrays;
4+
5+
/**
6+
* Created by 周杰伦 on 2018/4/6.
7+
*/
8+
public class 完全背包 {
9+
//基本解法
10+
//三重循环,效率低
11+
public int knapsack(int W, int N, int[] weights, int[] values) {
12+
int[][] dp = new int[N + 1][W + 1];
13+
for (int i = 1; i <= N; i++) {
14+
int w = weights[i - 1], v = values[i - 1];
15+
for (int j = 1; j <= W; j++) {
16+
for (int k = 0;k <= j/w;k ++) {
17+
if (j >= k * w) {
18+
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - k * w] + k * v);
19+
} else {
20+
dp[i][j] = dp[i - 1][j];
21+
}
22+
}
23+
}
24+
}
25+
return dp[N][W];
26+
}
27+
28+
//转化为01背包的解法
29+
//即F[i][j]=F[i][j-C[i]]+W[i]。为什么会是F[i][j-C[i]]+W[i]?
30+
// 因为我们前面已经最大限度的放了第i件物品,如果能放就放这最后的一件,
31+
//j的循环要正序,因为完全背包的第i个物品出现会影响前面的值。
32+
//即前面的值需要修改,以便于影响后面的值。
33+
public int knapsack03(int W, int N, int[] weights, int[] values) {
34+
int[][] dp = new int[N + 1][W + 1];
35+
for (int i = 1; i <= N; i++) {
36+
int w = weights[i - 1], v = values[i - 1];
37+
for (int j = w; j <= W; j++) {
38+
if (j >= w) {
39+
//注意和01背包的区别,这里是dp[i][j-need[i]]+value[i]
40+
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - w] + v);
41+
} else {
42+
dp[i][j] = dp[i - 1][j];
43+
}
44+
}
45+
}
46+
return dp[N][W];
47+
}
48+
49+
//优化成一维后解法 不必装满
50+
public int knapsack2(int W, int N, int[] weights, int[] values) {
51+
int[]dp = new int[W + 1];
52+
for (int i = 1; i <= N; i++) {
53+
int w = weights[i - 1], v = values[i - 1];
54+
for (int j = w; j <= W; j ++) {
55+
if (j >= w) {
56+
dp[j] = Math.max(dp[j], dp[j - w] + v);
57+
}
58+
}
59+
}
60+
return dp[W];
61+
}
62+
63+
//优化成一维后解法 必须装满
64+
public int knapsack3(int W, int N, int[] weights, int[] values) {
65+
int[]dp = new int[W + 1];
66+
Arrays.fill(dp, Integer.MIN_VALUE);
67+
dp[0] = 0;
68+
for (int i = 1; i <= N; i++) {
69+
int w = weights[i - 1], v = values[i - 1];
70+
for (int j = w; j <= W; j ++) {
71+
if (dp[j - w] != Integer.MIN_VALUE) {
72+
dp[j] = Math.max(dp[j], dp[j - w] + v);
73+
}
74+
}
75+
}
76+
return dp[W];
77+
}
78+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package DP.背包;
2+
3+
import java.util.Arrays;
4+
5+
/**
6+
* Created by 周杰伦 on 2018/4/6.
7+
*/
8+
public class 背包01不用装满 {
9+
public static void main(String[] args) {
10+
int N = 3;
11+
int W = 10;
12+
int w[] = {3, 4, 5};
13+
int v[] = {4, 5, 6};
14+
System.out.println(knapsack(W,N,w,v));
15+
System.out.println(knapsack2(W,N,w,v));
16+
}
17+
public static int knapsack(int W, int N, int[] weights, int[] values) {
18+
19+
int[][] dp = new int[N + 1][W + 1];
20+
//物品数为0时,取法初始化为0,代表不能装满,但是可以进行累加。因为不需要装满。
21+
Arrays.fill(dp[0], 0);
22+
for (int i = 1; i <= N; i++) {
23+
int w = weights[i - 1], v = values[i - 1];
24+
for (int j = 1; j <= W; j++) {
25+
if (j >= w) {
26+
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w] + v);
27+
} else {
28+
dp[i][j] = dp[i - 1][j];
29+
}
30+
}
31+
}
32+
return dp[N][W];
33+
}
34+
35+
// 优化一维 只对一维有这个要求
36+
//01背包每个物品只装一次,所以后面的结果不影响前面的结果。j应该逆序来算。
37+
//如果正序来算,后面的循环中可能会修改前面的值,这是不被允许的,因为一个物品只放一次。
38+
//后面的循环是不能改前面的值的。
39+
public static int knapsack2(int W, int N, int[] weights, int[] values) {
40+
int []dp = new int[W + 1];
41+
for (int i = 1; i <= N; i++) {
42+
int w = weights[i - 1], v = values[i - 1];
43+
for (int j = W; j >= w; j --) {
44+
dp[j] = Math.max(dp[j], dp[j - w] + v);
45+
}
46+
}
47+
return dp[W];
48+
}
49+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package DP.背包;
2+
3+
import java.util.Arrays;
4+
5+
/**
6+
* Created by 周杰伦 on 2018/4/7.
7+
*/
8+
public class 背包01需要装满 {
9+
public static void main(String[] args) {
10+
int N = 3;
11+
int W = 10;
12+
int w[] = {3, 4, 5};
13+
int v[] = {4, 5, 6};
14+
System.out.println(knapsack(W,N,w,v));
15+
System.out.println(knapsack2(W,N,w,v));
16+
}
17+
public static int knapsack(int W, int N, int[] weights, int[] values) {
18+
19+
int[][] dp = new int[N + 1][W + 1];
20+
//前0个物品永远无法装满1-W重量的背包。置为负无穷,dp累加时只考虑前面已经装满的清空。
21+
Arrays.fill(dp[0], Integer.MIN_VALUE);
22+
//前0个物品刚好可以装满容量为0的背包,此时价值为0,以此为初始值进行累加。
23+
dp[0][0] = 0;
24+
for (int i = 1; i <= N; i++) {
25+
int w = weights[i - 1], v = values[i - 1];
26+
for (int j = w; j <= W; j++) {
27+
if (j >= w && dp[i - 1][j - w] != Integer.MIN_VALUE) {
28+
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w] + v);
29+
}
30+
}
31+
}
32+
return dp[N][W];
33+
}
34+
35+
// 优化一维
36+
public static int knapsack2(int W, int N, int[] weights, int[] values) {
37+
int []dp = new int[W + 1];
38+
//前0个物品永远无法装满1-W重量的背包。置为负无穷,dp累加时只考虑前面已经装满的清空。
39+
Arrays.fill(dp, Integer.MIN_VALUE);
40+
//前0个物品刚好可以装满容量为0的背包,此时价值为0,以此为初始值进行累加。
41+
dp[0]= 0;
42+
for (int i = 1; i <= N; i++) {
43+
int w = weights[i - 1], v = values[i - 1];
44+
for (int j = W; j >= w; j --) {
45+
if (dp[j - w] != Integer.MIN_VALUE) {
46+
dp[j] = Math.max(dp[j], dp[j - w] + v);
47+
}}
48+
}
49+
return dp[W];
50+
}
51+
}

0 commit comments

Comments
 (0)