

- 问题可以采用递归的方法进行解决
 - 递归方法存在着重复计算的问题
 - 采用动态规划的方式解决,将重复的计算记录下来
 

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*/// 递归解法 能运行到45public 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<nfor(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;
    }
