动态规划练习(一)

参考 大佬的学习笔记 题解
题目全部基于leetcode

解题步骤

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

当前进度基础篇,持续更新。。。

基础篇

熟悉动态规划的解题思路,理解解题步骤

509.斐波那契数

题目描述

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n ,请计算 F(n) 。

解题步骤

  1. 确定dp数组(dp table)以及下标的含义
    dp:斐波那契数列
    dp[i]:第i个数的斐波那契数值
  2. 确定递推公式
    dp[i] = dp[i-1] + dp[i-2]
  3. dp数组如何初始化
    dp[0] = 0
    dp[1] = 1
  4. 确定遍历顺序
    由推导公式dp[i] = dp[i-1] + dp[i-2] 可知
    是由前数计算出后数的,顺序为从前往后遍历
  5. 举例推导dp数组
    0 1 1 2 3 5 8 13

实现代码

class Solution {
    public int fib(int n) {
        if (n<2) {
            return n;
        }
        return fib(n-1) + fib(n-2);
    }
}

70. 爬楼梯

题目描述

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

解题步骤

  1. 确定dp数组(dp table)以及下标的含义
    dp[i]:爬到第i层楼梯,有dp[i]种方法
  2. 确定递推公式

    i-2阶爬2个台阶,可达到i
    i-1阶爬1个台阶,可达到i
    可知
    dp[i] = dp[i-1] + dp[i-2]
  3. dp数组如何初始化
    爬上1阶,只有1种方法
    dp[1] = 1
    爬上2阶,有2种方法(1+1;2)
    dp[2] = 2
  4. 确定遍历顺序
    由推导公式dp[i] = dp[i-1] + dp[i-2] 可知
    是由前数计算出后数的,顺序为从前往后遍历
  5. 举例推导dp数组
    1 2 3 5 8 13

实现代码

class Solution {
    public int climbStairs(int n) {
        if (n < 3) {
            return n;
        }
        return climbStairs(n-1) + climbStairs(n-2);
    }
}

上面的递归方法,在leetcode会超出时间限制,可尝试下面的方法

class Solution {
    public int climbStairs(int n) {
        int dp0 = 0, dp1 = 0, result = 1;
       for (int i = 1; i <= n; ++i) {
           dp0 = dp1;
           dp1 = result;
           result = dp0 + dp1;
       }
       return result;
    }
}

746. 使用最小花费爬楼梯

题目描述

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。
请你计算并返回达到楼梯顶部的最低花费。

解题步骤

  1. 确定dp数组(dp table)以及下标的含义
    dp[i]:达到第i层楼梯的最低花费
  2. 确定递推公式

    一次可爬1or2台阶,花费有2种
    可知
    dp[i] = dp[i-1] + cost[i-1]

    dp[i] = dp[i-2] + cost[i-2]
    所以
    最低花费
    dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2])
    这样可保证每次都是最低花费
  3. dp数组如何初始化
    由题目可知
    可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯
    dp[0] = 0
    dp[1] = 0
  4. 确定遍历顺序
    由推导公式可知
    是由前数计算出后数的,顺序为从前往后遍历
  5. 举例推导dp数组

输入:cost = [1,100,1,1,1,100,1,1,100,1]
输出:6
解释:你将从下标为 0 的台阶开始。
-支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
-支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
-支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
-支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
-支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
-支付 1 ,向上爬一个台阶,到达楼梯顶部。
总花费为 6 。

实现代码

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int dp0 =0, dp1 = 0, dpi = 0;
        for (int i=2; i<=cost.length; i++) {
            dpi = Math.min(dp1 + cost[i-1], dp0 + cost[i-2]);
            dp0 = dp1;
            dp1 = dpi;
        }
        return dpi;
    }
}

62. 不同路径

题目描述

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?

示例 1:
在这里插入图片描述
输入:m = 3, n = 7
输出:28

解题步骤

  1. 确定dp数组(dp table)以及下标的含义
    dp[i][j]:到达坐标(i,j)有d[i][j]种方法
  2. 确定递推公式
    由题目
    机器人每次只能向下或者向右移动一步
    可知
    dp[i][j] = d[i-1][j] + d[i][j-1]
  3. dp数组如何初始化
    d[0][j] = 1
    d[i][0] = 1
  4. 确定遍历顺序
    根据推导公式可从左向右遍历
  5. 举例推导dp数组

输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1.向右 -> 向下 -> 向下
2.向下 -> 向下 -> 向右
3.向下 -> 向右 -> 向下

实现代码

class Solution {
    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];
        dp[0][0] = 1;
        for (int i = 0; i < m; i++) {
            dp[i][0] = 1;
            for (int j = 0; j < n; j++) {
                dp[0][j] = 1;
                if (i > 0 && j > 0) {
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                }
            }
        }
        return dp[m-1][n-1];
    }
}

63. 不同路径 II

题目描述

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。

示例 1:
在这里插入图片描述
输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:

  1. 向右 -> 向右 -> 向下 -> 向下
  2. 向下 -> 向下 -> 向右 -> 向右

解题步骤

  1. 确定dp数组(dp table)以及下标的含义
    dp[i][j]:到达坐标(i,j)有d[i][j]种方法
  2. 确定递推公式
    有62题可知,在没有障碍的情况下
    dp[i][j] = d[i-1][j] + d[i][j-1]
    有障碍 d[i][j] = 0
  3. dp数组如何初始化
    没有障碍物的情况下
    d[0][j] = 1,d[i][0] = 1
  4. 确定遍历顺序
    根据推导公式可知从左向右遍历
  5. 举例推导dp数组
    示例1

实现代码

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length, n = obstacleGrid[0].length;
        int[][] dp = new int[m][n];
        // fix 起点 终点为障碍物
        if (obstacleGrid[m - 1][n - 1] == 1 || obstacleGrid[0][0] == 1) {
            return 0;
        }
        // 初始化
        for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) {
            dp[i][0] = 1;
        }
        for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) {
            dp[0][j] = 1;
        }
        
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = (obstacleGrid[i][j] == 0) ? dp[i - 1][j] + dp[i][j - 1] : 0;
            }
        }
        return dp[m - 1][n - 1];
    }
}

343. 整数拆分

题目描述

给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。
返回 你可以获得的最大乘积 。

示例 1:

输入: n = 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。

示例2

输入: n = 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

解题步骤

  1. 确定dp数组(dp table)以及下标的含义
    dp[i]:输入数字i,输出最大乘积dp[i]
  2. 确定递推公式
    i 拆分为 j和i-j
    dp[i] = max(j * (i-j), j * dp[i-j]) 确定dp[i]为最大值
    因为i j是变动的
    所以
    dp[i] = max(dp[i], max(j*(i-j), j*dp[i-j]))
  3. dp数组如何初始化
    dp[1]=1 dp[2] = 1
  4. 确定遍历顺序
    从小到大遍历
  5. 举例推导dp数组
    dp[2] = 1
    dp[3] = 1 * 2
    dp[4] = 2 * 2
    dp[5] = 2 * 3
    dp[6] = 3 * 3
    dp[7] = 3 * 4
    dp[8] = 2 * 3 * 3
    dp[9] = 3 * 3 * 3
    dp[10] = 3 * 3 * 4

实现代码

class Solution {
    public int integerBreak(int n) {
        int[] dp = new int[n+1];
        dp[2] = 1;
        for(int i = 3; i <= n; i++) {
       		for(int j = 1; j <= i; j++) {
       		// 由数学知识可知,数字n拆分为相近的数可以最大,可以如下优化
            // for(int j = 1; j <= i/2+1; j++) {
                dp[i] = Math.max(dp[i], Math.max(j*(i-j), j*dp[i-j]));
            }
        }
        return dp[n];
    }
}

96. 不同的二叉搜索树

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值