力扣刷题-动态规划算法1:

知识点介绍:

  1. 动态规划和贪心算法的直观区别:
    1)动态规划:后一步需要依赖于前一步的操作
    2)贪心:后一步与前一步选择无关,每一步都需要达到最优,才能得到全局最优。
  2. 动态规划的解题步骤:
    1)确定dp数组(dp [ i ] )以及下标的含义 :
    2)确定递推公式
    3)dp数组如何初始化
    4)确定遍历顺序
    5)举例推导dp数组

题目一:509. 斐波那契数

  1. 题目说明
  2. 求解思路
    1)可以使用递归的方法去做
    2)可以使用动态规划的方法做,得到一个一维的数组,每个位置装着索引n对应的斐波那契数。
  3. 求解步骤
    1)首先判断dp[i]数组的含义是什么:dp[i],f(n)的菲波那切数列。
    2)得到dp数组的迭代式:
    3)dp数组的初始化
    4)dp数组的遍历方式
    5)举一个例子进行操作。
  4. 代码展示
//采用递归的方法
    public int fib(int n) {
        if(n<=1) return n;
        return fib(n-1)+fib(n-2);

    }
//采用动态规划:使用一个矩阵
    public int fib(int n) {
        if(n<=1) return n;
        //确定dp数组的含义
        int[] dp=new int[n+1];
        //初始化
        dp[0]=0;
        dp[1]=1;
        //遍历顺序+递推公式
        for(int i=2;i<=n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        System.out.println("dp[n]="+Arrays.toString(dp));
        return dp[n];
    }
  1. 补充说明

按照步骤一步一步来就行了。

题目二:70. 爬楼梯

  1. 题目说明

  2. 求解思路
    爬楼梯,

  3. 求解步骤

  4. 代码展示

//这个题目就是斐波那契的翻版
        //步骤一:dp[i]的含义是:爬到第i层,有多少种方法
        //步骤二:确认递推公式:dp[i]=dp[i-1]+dp[i-2];
        //步骤三:初始化,i从一开始,故只需要讨论dp[1]=1和dp[2]=2
        //步骤四:遍历的顺序,从前向后遍历 
        //步骤五:自己举例说明
        if(n<=2) return n;
        //初始化:
        int[] dp=new int[n+1];
        dp[1]=1;
        dp[2]=2;
        for(int i=3;i<=n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];

    }
  1. 补充说明

题目三:746. 使用最小花费爬楼梯

  1. 题目说明
  2. 求解思路
  3. 求解步骤
  4. 代码展示
    public int minCostClimbingStairs(int[] cost) {
        //dp[i]的含义:在步骤i位置的最小花费
        //初始化
        int[] dp=new int[cost.length+1];
        dp[0]=0;
        dp[1]=0;
        //遍历(从前向后)
        for(int i=2;i<=cost.length;i++){
            dp[i]=Math.min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
        }
        return dp[cost.length];
    }
  1. 补充说明

题目四:62.不同路径

  1. 题目说明
  2. 求解思路
  3. 求解步骤
  4. 代码展示
public int uniquePaths(int m, int n) {
        //这需要用一个二维数组装:dp[i][j]每个位置的最大路径数
        //初始化:
        int[][] dp=new int[m][n];
        for(int i=0;i<n;i++){
            dp[0][i]=1;
        }
        for(int j=0;j<m;j++){
            dp[j][0]=1;
        }
        //遍历顺序
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                dp[i][j]=dp[i-1][j]+dp[i][j-1];
            }
        }
        System.out.println("dp="+Arrays.deepToString(dp));
        return dp[m-1][n-1];
    }
  1. 补充说明

题目五:63. 不同路径 II

  1. 题目说明
  2. 求解思路
  3. 求解步骤
  4. 代码展示
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        //与不同路径问题的区别就是,当出现障碍的地方,路径数变为0
        //这需要用一个二维数组装:dp[i][j]每个位置的最大路径数

        //初始化:
        int m=obstacleGrid.length;
        int n=obstacleGrid[0].length;
        int[][] dp=new int[m][n];
        for(int i=0;i<n;i++){   //注意点:如果在初始化的位置出现障碍,后面也都为0
            if(i==0) {
                dp[0][i]=obstacleGrid[0][i]==1?0:1;
            }else{
                dp[0][i]=obstacleGrid[0][i]==1?0:dp[0][i-1];
            }
        }
        for(int j=1;j<m;j++){
            dp[j][0]=obstacleGrid[j][0]==1?0:dp[j-1][0];
        }
        System.out.println("dp="+Arrays.deepToString(dp));

        //遍历顺序
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                dp[i][j]=obstacleGrid[i][j]==1?0:dp[i-1][j]+dp[i][j-1];
            }
        }
        System.out.println("dp="+Arrays.deepToString(dp));
        return dp[m-1][n-1];
    }
  1. 补充说明

题目六:343. 整数拆分

  1. 题目说明
  1. 求解思路
    这道题可以作为一道数学题目,用到了贪心的思想,每一步,尽可能多的三 ,就能够达到最大。
  2. 求解步骤
    1)当数字小于四的时候,可以直接得出结果
    2)当数字大于四的时候,尽可能多的三,然后剩余的留下来。
    3)以上的结论可以通过自己举例子观察得到。
  3. 代码展示
//贪心算法
public int integerBreak(int n) {
        //纯数学方法,尽可能多3,在数大于四的时候
        if(n<=3) return n-1;
        //
        int a=1;
        while(n>4){
            n=n-3;
            a=a*3;
        }
        return a*n;
    }
  1. 补充说明
    纯粹的数学方法求解,当数大于四的时候,尽可能多的三。

题目七:96.不同的二叉搜索树

  1. 题目说明
  2. 求解思路
    对于动规这类题目,首先是找到动规表达式的意义:
    1)dp[i]表示i个节点组成不同二叉搜索树的个数
    2)然后就去试一试,dp[0],dp[1],dp[2].dp[3]分别为多少,推导出表达是式子。
    3)dp[0]=1; dp[1]=1 ; dp[2] =2 ;dp[3] =dp[0]*dp[2]+dp[1]*dp[1]+dp[2]*dp[0] 故公式就出来了
    4)dp[i]=dp[0]*dp[i-1]+dp[1]*dp[i-2]+dp[2]*dp[i-2]+…dp[i-1]*dp[0];
  3. 求解步骤
    1)推测dp数组的含义
    2)推导dp数组的迭代式
    3)dp数组的初始化操作
    4)dp数组的遍历方式
    5)拿一个案例测试一下
  4. 代码展示
class Solution {
    public int numTrees(int n) {
        //1)特殊情况先处理
        if(n<=2) return n;
        //2)dp数组初始化
        int[] dp=new int[n+1];
        dp[0]=1;
        dp[1]=1;
        dp[2]=2;
        for(int i=3;i<=n;i++){
            for(int j=0;j<=i-1;j++){
                dp[i]+=dp[j]*dp[i-1-j];
            }
        }
        return dp[n];
        
    }
}
  1. 补充说明
    1)稿明白dp数组的意义;
    2)还有就是找规律是核心;可以通过一个个例子来推导公式。(不需要证明)

总结:动态规划基本步骤

  1. 今天这几道题目都是比较简单的,所以可能用不到动规的五步曲,但是一定要牢记,后续的题目一定会用上的。
  2. 动规五步走
    1)dp定义:确定dp数组(dp [ i ] ,dp[ i ][ j ])以及下标的含义
    2)dp数组的递归表达式:根据前者如何推断出后者的情况。
    3)dp数组初始化:最初始的几个情况是什么?
    4)dp数组的遍历方式:怎么遍历?
    5)举例推导dp数组:自己举一个简单的例子,看看迭代过程是不是自己期望的。
  3. 代码写出来出错怎么办?灵魂三问
    1)这道题我举例推导出递推公式了吗?
    2)我打印出来中间的dp数组日志了吗?
    3)我打印出来的日志和我期望中的日志是不是一样的?
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值