代码随想录算法训练营第四十一天 _ 动态规划_343. 整数拆分、96.不同的二叉搜索树、01背包问题。

学习目标:

动态规划五部曲:
① 确定dp[i]的含义
② 求递推公式
③ dp数组如何初始化
④ 确定遍历顺序
⑤ 打印递归数组 ---- 调试
引用自代码随想录!

60天训练营打卡计划!

学习内容:

343. 整数拆分

  • 动态规划五步曲:
    ① 确定dp[i]的含义 : 对i拆分后 得最大乘积为dp[i]
    ② 求递推公式 :
    Ⅰ j * dp[i - j] ,其中dp[i - j]代表两个数及以上的最大乘积。我根本不需要关心dp[i - j]是怎么组成的,因为题目只要求求出拆分后的最大的乘积,并没有问什么样的拆分结果可以获取拆分后的最大乘积。
    Ⅱ j * (i - j) 代表拆为两个数,两个数的乘积
    Ⅲ 所以 dp[i] = max(j * dp[i - j] , j * (i - j) , dp[i]) ---- 因为dp[i]代表的是拆分后的最大的乘积,所以一定是从既有的dp[i] 结果以及有潜在可能性有最大值的结果中取出最大值。
    ③ dp数组如何初始化 : dp[0] = 0 dp[1] = 0 dp[2] = 1
    ④ 确定遍历顺序 : 从前向后
  • 在实现的过程中还有一些小的技巧,比如说拆分的时候只需要使j 遍历到 i/2 的位置即可,最大值一定会落到这个范围中来。
class Solution {
    public int integerBreak(int n) {
        // 申请空间时一定要多申请一位!
        int[] dp = new int[n + 1];
        dp[0] = 0;
        dp[1] = 0;
        dp[2] = 1;
        if(n == 2)  return dp[n];
        // dp数组的值依赖所求项的前n-1项,故需要逐个求解dp数组的值。
        for(int i = 3; i <= n; i++){
            for(int j = 0; j <= i/2; j++){
                dp[i] = Math.max(j * dp[i - j], j * (i - j)) > dp[i] ? 
                Math.max(j * dp[i - j], j * (i - j)) :  dp[i];
            }
        }
        return dp[n];
    }
}

96.不同的二叉搜索树

  • 二叉搜索树:使用中序遍历可以得到一个递增的有序数组。

  • 需要画出1 2 3三种情况中二叉搜索树的情况,要观察递推公式。
    dp[3] = dp[0]*dp[2] +dp[1]*dp[1]+dp[2]*dp[0]

  • 动态规划五步曲:
    ① 确定dp[i]的含义 : 第i个数可以构造出的二叉搜索树的个数。
    ② 求递推公式 : dp[i] = dp[j-1]*dp[i-j] ( j 从 1 增到 i ,求和 )
    ③ dp数组如何初始化 : dp[0] = 1 dp[1] = 1 dp[2] = 2 dp[3] = 5
    ④ 确定遍历顺序 : 从小向大

// 动态规划
class Solution {
    public int numTrees(int n) {
        int[] dp = new int[n+1];
        // dp[0]一定是1
        dp[0] = 1;
        dp[1] = 1;
        if(n == 1)   return dp[n];
        dp[2] = 2;
        if(n == 2)   return dp[n];
        for(int i = 3; i <= n; i++){
            for(int j = 1; j <= i; j++){
                dp[i] += dp[j-1] * dp[i-j];
            }
            // System.out.println(dp[i]);
        }
        return dp[n];
    }
}

二维数组处理01背包问题

  • 听起来思路很简单,但其实一点也不好实现。
  • 动态规划五步曲:
    ① 确定dp[i][j]的含义 : 任取[0, i]的物品后放进容量为j的背包 所能放的 最大价值
    ② 求递推公式 : dp[i][j] = max(dp[i-1][j] , dp[i-1][ j - weight[i] ] + value[i])
    Ⅰ 不放物品 i : dp[i-1][j]
    Ⅱ 放物品 i : dp[i-1][j - weight[i]] + value[i]
    ③ dp数组如何初始化 : 按下表的第一行和第一列赋值,其中箭头都是继承来的值,画圈的表示自己取得了最大值。请添加图片描述
    ④ 确定遍历顺序 : 先物品后背包(行) / 先背包后物品(列)
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        //m,n分别代表物品种类和背包容量
        int itemSize = 0,bagSize = 0;
        Scanner sc = new Scanner(System.in);
        //获取itemSize和bagSize的值
        itemSize = sc.nextInt();
        bagSize = sc.nextInt();
        //初始化对应的重量数组和价值数组
        int[] weight = new int[itemSize];
        int[] value = new int[itemSize];
        //这两个都是物品的属性,大小只和物品数量有关
        for(int i = 0;i < itemSize;i++){
            weight[i] = sc.nextInt();
        }
        for (int i = 0;i < itemSize;i++){
            value[i] = sc.nextInt();
        }
        
        // int[] weight = {1,3,4};
        // int[] value = {15,20,30};
        // int bagSize = 4;
        testWeightBagProblem(weight,value,bagSize);
    }

    /**
     * 动态规划获得结果
     * @param weight  物品的重量
     * @param value   物品的价值
     * @param bagSize 背包的容量
     */
    public static void testWeightBagProblem(int[] weight, int[] value, int bagSize){

        int itemSize = weight.length;
        // dp数组的含义是:在[0,i]件物品中选择是否放入背包 的 最大价值
        int[][] dp = new int[itemSize][bagSize+1];
        
        // 初始化dp数组,默认都为0.
        // 只放一件物品时的初始化
        for(int j = weight[0]; j < bagSize+1; j++){
            dp[0][j] = value[0];
        }
        
        // 正常的为dp数组赋值,依赖左上位置的其他的dp值
        for(int i = 1; i < itemSize; i++){
            // j是背包容量
            for(int j = 1; j < bagSize+1; j++){
                // 如果容量不够放入新的物品,则从上一行继承
                if(j < weight[i])   dp[i][j] = dp[i-1][j];
                // 如果容量可以放入新的物品,则从上一行的左侧继承
                else
                    dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]);
            }
        }
        System.out.println(dp[itemSize-1][bagSize]);
        
        // 打印dp数组
        // for (int i = 0; i < goods; i++) {
        //     for (int j = 0; j <= bagSize; j++) {
        //         System.out.print(dp[i][j] + "\t");
        //     }
        //     System.out.println("\n");
        // }
    }
}

一维数组处理01背包问题

  • 动态规划五步曲:
    ① 确定dp[j]的含义 : 任取物品放进容量为j的背包 所能放的 最大价值
    ② 求递推公式 : dp[j] = max(dp[j] , dp[j - weight[i]] + value[i])
    Ⅰ 不放物品 i : dp[j]
    Ⅱ 放物品 i : dp[j - weight[i]] + value[i]
    ③ dp数组如何初始化 : 初始值全部附0,长度为容量的长度加1(j+1)
    ④ 确定遍历顺序 : 必须先物品后背包(行),且便利背包大小时,必须使用倒序的顺序遍历。(为了防止一个物品被使用多次,倒叙遍历时相同的物品仅能被取用一次)

请添加图片描述

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        //m,n分别代表物品种类和背包容量
        int itemSize = 0,bagSize = 0;
        Scanner sc = new Scanner(System.in);
        //获取itemSize和bagSize的值
        itemSize = sc.nextInt();
        bagSize = sc.nextInt();
        //初始化对应的重量数组和价值数组
        int[] weight = new int[itemSize];
        int[] value = new int[itemSize];
        //这两个都是物品的属性,大小只和物品数量有关
        for(int i = 0;i < itemSize;i++){
            weight[i] = sc.nextInt();
        }
        for (int i = 0;i < itemSize;i++){
            value[i] = sc.nextInt();
        }
        
        // int[] weight = {1,3,4};
        // int[] value = {15,20,30};
        // int bagSize = 4;
        testWeightBagProblem(weight,value,bagSize);
    }

    /**
     * 动态规划获得结果
     * @param weight  物品的重量
     * @param value   物品的价值
     * @param bagSize 背包的容量
     */
    public static void testWeightBagProblem(int[] weight, int[] value, int bagSize){

        // 创建dp一维数组
        int goods = weight.length;  // 获取物品的数量
        int[] dp = new int[bagSize + 1];

        // 初始化dp数组
        // 创建数组后,其中默认的值就是0
        
        // 填充dp数组
        for (int i = 0; i < goods; i++) {
            // 必须使用倒叙遍历背包大小
            for (int j = bagSize; j > 0; j--) {
                // 防止越界错误
                if (j < weight[i]) {
                    dp[j] = dp[j];
                } else {
                    dp[j] = Math.max(dp[j] , dp[j-weight[i]] + value[i]);
                }
            }
        }
        
        System.out.print(dp[bagSize]);

        // 打印dp数组
        // System.out.print(dp[goods-1][bagSize] + "\n");
        // for (int i = 0; i < goods; i++) {
        //     for (int j = 0; j <= bagSize; j++) {
        //         System.out.print(dp[i][j] + "\t");
        //     }
        //     System.out.println("\n");
        // }
    }
}

在这里插入图片描述


学习时间:

  • 上午两个半小时,整理文档半小时。

你可能感兴趣的:(刷题训练心得,算法,动态规划)