- 问题可以采用递归的方法进行解决
- 递归方法存在着重复计算的问题
- 采用动态规划的方式解决,将重复的计算记录下来
1.dp数组的定义和下标。
2.递推公式。
3.dp数组如何初始化,初始化也需要注意。
4.遍历顺序,比较考究,01 先遍历背包,后遍历物品。
4.1排列和组合的遍历顺序是不相同的。
4.1.1 排列:背包在外 物品在内。(322)
4.1.2 组合:物品在外,背包在内。(518)
5.(出现问题)打印dp数组。(打印dp数组,检查是否有问题,检验1 2 3 4 步骤)
举个栗子
Leetcode.70.爬楼梯
// 70. 爬楼梯
/**
* f(10) = f(9)+f(8)
* f(9) = f(8) + f(7)
* ......
* f(3) = f(2) + f(1)
* f(2) = f(1)+1
* f(1) = 1
*/
// 递归解法 能运行到45
public static int climbStairs1(int n) {
if(n == 2){
return 2;
}
if(n == 1){
return 1;
}
return climbStairs1(n-1)+climbStairs1(n-2);
}
//动态规划解法
// 使用累加的方式,将上一个值记住
// 重复的值不需要重复的相加
public static int climbStairs2(int n) {
if(n == 2){
return 2;
}
if(n == 1){
return 1;
}
int count = 0;
int a = 1;
int b = 2;
for(int i = 3;i<=n;i++){
count = a+b; //累加结果 契合总结的公式 下一个总数是前两个相加
//向下迭代
a = b; //a成为上上个台阶 下次迭代的第n-2个台阶的走法等于上次迭代n-1个台阶的走法
b = count; //b成为上一个台阶 下次迭代的第n-1个台阶的走法等于上次迭代的第n个台阶走法
}
return count;
}
示例
题:509. 斐波那契数(注意边界问题)
public int fib(int n) {
if(n == 0){return 0; }
if(n == 1){ return 1; }
int[] dp = new int[n+1];
dp[0] = 0;
dp[1] = 1;
// 注意边界问题,是 i<=n 还是 i<n
for(int i=2;i<=n;i++){
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
题:5. 最长回文子串
//双层循环
public String longestPalindrome(String s) {
String res = s.substring(0,1);
int n = s.length();
boolean[][] dp = new boolean[n][n];
for(int i=n-1;i>=0;i--){
for(int j=i;j<n;j++){
// 开头等于末尾并且(三个字符或者开头与结尾之间的字符串(正下方)是回文)
if(s.charAt(i) == s.charAt(j) &&(j-i<=2 || dp[i+1][j-1])){
dp[i][j] = true;
res = s.substring(i,j+1).length()>res.length()?s.substring(i,j+1):res;
}
}
}
return res;
}
循序渐进
- 爬楼梯
- 背包问题
- 打家劫舍
- 股票问题
- 子序列问题
- 边距距离问题
01背包问题
01背包问题的模版 ```java //01背包 for (int i = 0; i < n; i++) { for (int j = m; j >= V[i]; j—) {
} } //完全背包 for (int i = 0; i < n; i++) { for (int j = V[i]; j <= m; j++) {f[j] = max(f[j], f[j-V[i]] + W[i]);
} }f[j] = max(f[j], f[j-V[i]] + W[i]);
/ f[j]代表当前背包容量为j的时候,可以获取的最大价值。 完全背包是从左向右遍历,f[j-V[i]]取到的是拿第i个物品时的值,是新值, 可以重复无限的拿,f[j]的值也会随之增加。 V:商品的体积 W:商品的价值 /
<a name="Civ7M"></a>
### No经典01背包问题
```java
public class No经典01背包问题 {
/**
* 问题描述:
* 现有4个物品,小偷背包的容量为8,怎么样能偷到价值最多的东西?
* 如:
* 物品表号: 1 2 3 4
* 物品重量: 2 3 4 5
* 物品价值: 3 4 5 8
*/
public static void main(String[] args) {
int[] nums1 = {2,3,4,5};
int[] nums2 = {3,4,5,8};
System.out.println(rob(nums1,nums2,8));
}
/**
* 1. 动态规划 01背包问题
* 画表格可以辅助理解
* @param nums1
* @param nums2
* @param sum
* @return
*/
public static int rob(int[] nums1, int[] nums2, int sum){
int len1 = nums1.length;
int[][] dp = new int[len1][sum+1]; //价值
for(int i=0;i<len1;i++){
for(int j=0;j<=sum;j++){
if(nums1[i] > j){
if(i==0){ //初始化边界
dp[i][j] = 0;
}else{
dp[i][j] = dp[i-1][j];
}
}else {
if(i==0){ //初始化边界
dp[i][j] = nums2[i];
}else {
dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-nums1[i]]+nums2[i]);
}
}
}
}
return dp[len1-1][sum];
}
/**
* 2. 暴力解法 使用回溯遍历 时间复杂度为 2的n次方
*/
}
416. 分割等和子集
public static boolean canPartition(int[] nums) {
int sum = 0;
for (int n : nums) {
sum += n;
}
if(sum % 2 != 0) return false;//整数相加不可能得小数
int W = sum / 2;//相当于背包总承重
int [] dp = new int[W+1]; //dp[i] 表示实现i 的情况有几种,w+1 是为了与w和数组下标对应
dp[0] = 1; //实现0 的情况有一种
for (int num : nums) {
for (int i = W; i >= num; i--) {
dp[i] += dp[i-num];
}
}
return dp[W] != 0;
}