- 动态规划一般使用的是记忆化搜索对时间复杂度进行优化。
- 动态规划可以在选择某个条件时会出现两个或多个不同分支的结果时使用。
- 动态规划里面dp数组存储的是权重。
- 动态规划里面那一步有判断大小的操作中,比较双方是增加当前步骤权重和保持原来权重的双方。
一、01背包
如果我们使用普通的不存储中间结果的方法。由于在解题的时候采用递归算法而且有点树形结构,所以我们会发现在计算最终结果的过程中会出现重复计算中间结果的现象。
下面是2个01背包的写法
import java.util.*;
public class DP01 {
static int n;
static int w[] = new int[1000];
static int v[] = new int[1000];
static int W;
static int dp[][] = new int[100][10000];//这个数组的y不是连续的。
public static int rec(int i,int j) {//j指的是还剩余多少空间
int res;
if(dp[i][j]>=0)
return dp[i][j];
if(i == n)
res = 0;
else if(w[i]>j)
res = rec(i+1,j);
else
res = Math.max(rec(i+1,j), rec(i+1,j-w[i])+v[i]);
dp[i][j] = res;
return dp[i][j];
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for(int i = 0;i<n;i++) {
w[i] = sc.nextInt();
v[i] = sc.nextInt();
}
W = sc.nextInt();
for(int i = 0;i<=n;i++)
for(int j = 0;j<=W;j++)
dp[i][j] = -1;
System.out.print(rec(0,W));
}
}
上下两份代码的区别主要在 ** j ** 的含义上面。
import java.util.Scanner;
public class DP0101 {
static int n;
static int w[] = new int[1000];
static int v[] = new int[1000];
static int W;
static int dp[][] = new int[100][10000];//这个数组的y不是连续的
public static void main(String[] args) {
// TODO 自动生成的方法存根
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for(int i = 0;i<n;i++) {
w[i] = sc.nextInt();
v[i] = sc.nextInt();
}
W = sc.nextInt();
for(int i = 0;i<=n;i++)
for(int j = 0;j<=W;j++)
dp[i][j] = 0;
for(int i =0;i<n;i++) {
for(int j = 0;j<=W;j++) {//这里的j指的是当前物品的重量是否小于该j。
if(j<w[i])
dp[i+1][j] = dp[i][j];//和没加1前一样的价值权重
else
dp[i+1][j] = Math.max(dp[i][j], dp[i][j-w[i]]+v[i]);//这里的原理要清楚
}
}
System.out.print(dp[n][W]);//递推后的最终结果在二维数组右下角
}
}
二、题目应用
最长公共子序列、背包问题、鸡蛋掉落(掉落问题)
三、背包九讲
强烈推荐,真的看完受益匪浅!网页链接,必看!!
四、感想
- 在动态规划中,这一步的计算基础是根据上一步的计算得到的。
- 在我们计算的价值函数f(x)中,x的取值是不连续的,而且如果出现重量x相同的时候f(x)会被覆盖。
- 根据以上2点可以将动态规划从二维压缩到一维。详见视频也可以配合上方网页链接食用!
- 关于01背包的一维数组优化
for (int i = 1; i <= n; i++)
for (int j = V; j >= w[i]; j--)
f[j] = max(f[j], f[j - w[i]] + v[i]);
内循环之所以需要倒序是因为每次j-w[i]的值都是上一轮循环中更新的,即可以保证 f[j - w[i]] 是属于i-1个物品情况下的。
- 关于完全背包的一维数组优化
for (int i = 1; i <= n; i++)
for (int j = w[i]; j <= V; j++)
f[j] = max(f[j], f[j - w[i]] + v[i]);
在完全背包问题中没有限制上一轮循环中的物品一定不是这一轮循环的物品,所以从头到尾的内层循环适用于完全背包。