动态规划算法题刷题笔记

首先看动态规划的三要素:重叠子问题、最优子结构和状态转移方程。

重叠子问题:存在大量的重复计算

最优子结构:

状态转移方程:当前状态转移成以前的状态

动态规划的解题步骤主要有:

  • 确定 dp 数组以及下标的含义
  • 状态转移方程、递推公式
  • dp数组初始化、遍历顺序
  • 写代码验证

直接看实际的算法题

1.LeetCode70. 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 12 个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 1:

输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶

实际上就是斐波那契算法,我们按最后一次爬楼梯的情形:只有爬1个或者2个台阶,如下图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以状态转移方程就是 f(n) = f(n-1) + f(n-2)

public int climbStairs(int n) {
    if(n == 1) {
        return 1;
    }
    int[] dp = new int[n + 1];
    dp[0] = 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、2、3、4…m 级台阶,如何求最后的次数?根据上面的图可以得到状态方程:

f(n) = f(n-1) + f(n-2) +...+f(n - m)所以解题代码为:

public int climbStairs2(int n) {
    int[] dp = new int[n+1];
    dp[0] = 1;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; i <= m; j++) {
            if(i - j >= 0) {
                dp[i] = dp[i - j];
            }
        }
    }
    return dp[n];
}
2.LeetCode746. 使用最小花费爬楼梯

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

只能选择从下标为 0 或下标为 1 的台阶开始爬楼梯,

请你计算并返回达到楼梯顶部的最低花费。

示例 1:

输入:cost = [10,15,8]
输出:15
解释:你将从下标为 1 的台阶开始。

- 支付 15 ,向上爬两个台阶,到达楼梯顶部。
 总花费为 15 。

这题的思路和上一题爬楼梯很像,我们还是自顶向下来考虑,

如果最后一次选择记为f(n) 那么需要考虑f(n-1)f(n-2)谁更小,然后再加上当前的花费值。那么状态转移方程为:f(n) = min(f(n-1), f(n-2)) + cost[n]。因此代码就容易了:

public int minCostClimbingStairs(int[] cost) {
    int[] dp = new int[cost.length];
    dp[0] = cost[0];
    dp[1] = cost[1];
    for(int i = 2; i < cost.length; i++) {
        dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i];
    }
    //因为可以一次性爬两层,所以最后再比较倒数一次和倒数第二次的爬楼梯花费
    return Math.min(dp[cost.length - 1], dp[cost.length - 2]);
}
3.LeetCode62. 不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

示例 1:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

输入:m = 3, n = 7
输出:28

看题目可以知道,和前两题不一样,需要我们从两个维度去考虑。因此可以选择二维dp数组来解决问题,按照动态规划解题步骤:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 确定dp数组的含义
    • dp[m][n] 表示从 [0][0][m][n] 的路径条数,数组内的下标表示所处的位置
  2. 确定状态转移方程
    • 我们知道当前位置状态来源于左侧和上侧位置状态因此可以写成:
    • dp[m][n] = dp[m-1][n] +dp[m][n-1]
  3. 确定dp数组初始状态和遍历顺序
    • 初始状态要注意,和一维dp数组考虑一个初始值不同。我们要考虑二维多个初始值:
      • dp[0][i]dp[j][0] 这两列上,路径条数都应该为1
    • 遍历顺序,按照从小到大的原则进行

综合上面的思路,可以写出代码

public int uniquePaths(int m, int n) {
    int[][] dp = new int[m][n];
    //初始化dp数组
    for(int i = 0; i < m; i++) dp[i][0] = 1;
    for(int j = 0; j < n; j++) dp[0][j] = 1;
    //遍历顺序
    for(int i = 1; i < m; i++) {
        for(int j = 1; j < n; j++) {
            //状态转移方程
            dp[i][j] = dp[i][j - 1] + dp[i - 1][j];
        }
    }
    return dp[m - 1][n - 1];
}
4.LeetCode63. 不同路径 II

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。

示例 1:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

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

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

这题和上一题的区别就是有了障碍,那么我们该如何考虑这个障碍呢?主要分成两个方面:

  • 初始化时遇到障碍:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 顺序遍历时遇到障碍:

    也就是题目给出的情况,这种情况将障碍处设置为0 即可

其余的和上一题类似,所以直接给出代码:

public int uniquePathsWithObstacles(int[][] obstacleGrid) {
    // 数组的行数
    int m = obstacleGrid.length;
    // 数组的列数
    int n = obstacleGrid[0].length;
    int[][] dp = new int[m][n];
    //障碍及后面方格统一初始化
    for(int i = 0; i < m; i++) {
        if(obstacleGrid[i][0] == 1) {
            break;
        }
        dp[i][0] = 1;
    }
    for(int j = 0; j < n; j++) {
        if(obstacleGrid[0][j] == 1) {
            break;
        }
        dp[0][j] = 1;
    }
    for(int i = 1; i < m; i++) {
        for(int j = 1; j < n; j++) {
            //遍历过程遇见障碍,直接跳过
            if(obstacleGrid[i][j] == 1) {
                dp[i][j] = 0;
                continue;
            }
            dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
        }
    }
    return dp[m - 1][n - 1];
}
5.leetCode343. 整数拆分

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

返回 你可以获得的最大乘积

示例 1:

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

我们按照动态规划解题步骤一步步来:

  • 1.确定dp数组的含义以及下标:

    • dp数组表示的是最大乘积,下标表示整数n
  • 2.确定状态转移方程:

    • 考虑到一个整数至少要分成2个及以上的整数,所以:

      • 分成2个可以表达成 i * j
      • 分成2个以上可以表达成 j * dp[i - j]
    • 所以转移方程应该为: dp[i] = max(j * dp[i - j], j * i)

      • 但是这里我们会发现得不到符合题意的值:

        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

        因为每次取的都是最后一个,比如 dp[10] = max(9 * dp[1], 9 * 1) ,所以每次需要与前面的 dp[i] 比较,得到最后的最大值。

      • 因此转移方程应为:dp[i] = max(dp[i],max(j * dp[i - j], j * (i - j)))

  • 3.确定初始值,遍历顺序

    • dp[2] = 1,dp[1] = 1,dp[0] = 1 (dp[0] 和 dp[1] 实际上不符合题意,虽然赋值1 不影响结果)
    • i从3到n 遍历,j 从 1 到 i - 2 遍历
  • 4.根据上面三个步骤写代码:

public int integerBreak(int n) {
    int[] dp = new int[n + 1];
    //初始化赋值
    dp[2] = 1;
    for(int i = 3; i <= n; i++) {
        //从下标为2开始遍历
        for(int j = 1; j < i - 1; j++) {
            dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
        }
    }
    return dp[n];
}
6.LeetCode96. 不同的二叉搜索树

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

示例 1:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

输入:n = 3
输出:5

这题确实不好解决,二叉搜索树的种树这个变量不好确定。需要以左右子树为基础进行考虑,下面引用代码随想录的思路:

dp[3],就是 元素1为头结点搜索树的数量 + 元素2为头结点搜索树的数量 + 元素3为头结点搜索树的数量

元素1为头结点搜索树的数量 = 右子树有2个元素的搜索树数量 * 左子树有0个元素的搜索树数量

元素2为头结点搜索树的数量 = 右子树有1个元素的搜索树数量 * 左子树有1个元素的搜索树数量

元素3为头结点搜索树的数量 = 右子树有0个元素的搜索树数量 * 左子树有2个元素的搜索树数量

有2个元素的搜索树数量就是dp[2]。

有1个元素的搜索树数量就是dp[1]。

有0个元素的搜索树数量就是dp[0]。

所以dp[3] = dp[2] * dp[0] + dp[1] * dp[1] + dp[0] * dp[2]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以状态转移方程为 dp[i] += dp[j] * dp[i - j - 1];

初始值dp[0] = 1;dp[1] = 1,按照节点个数进行遍历。直接下出如下代码

public int numTrees(int n) {
    int[] dp = new int[n + 1];
    //初始化赋值
    dp[0] = 1;
    dp[1] = 1;
    for(int i = 2; i <= n; i++) {
        for(int j = 0; j < i; j++) {
            dp[i] += dp[j] * dp[i - j - 1];
        }
    }
    return dp[n];
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/352942.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HTML新手教程

HTML入门 教程&#xff1a;【狂神说Java】HTML5完整教学通俗易懂_哔哩哔哩_bilibili 一.初识HTML HyperTextMarkupLanguage&#xff08;超文本标记语言&#xff09; 超文本包括&#xff1a;文字、图片、音频、视频、动画。 HTML5的优势 世界知名浏览器厂商对HTML5的支持市场的…

Spring: alibaba代码规范校验工具checkstyle

文章目录 一、idea配置checkstyle插件二、激活CheckStyle三、配置自动格式化功能四、使用代码格式化 一、idea配置checkstyle插件 下载 Intellij IDEA Checkstyle 插件&#xff1a;File -> setting -> plugin通过关键字CheckStyle-IDEA搜索并安装。 安裝完成后重启idea…

【复现】万户ezoffice协同管理平台 任意文件读取漏洞_30

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 万户ezOFFICE协同管理平台分为企业版和政务版。 解决方案由五大应用、两个支撑平台组成&#xff0c;分别为知识管理、工作流程、沟…

Linux cat,tac,more,head,tail命令 查看文本

目录 一. cat 和 tac命令二. head 和 tail 命令三. more命令 一. cat 和 tac命令 cat&#xff1a;用来打开文本文件&#xff0c;从上到下的顺序显示文件内容。tac&#xff1a;用法和cat相同&#xff0c;只不过是从下到上逆序的方式显示文件内容。当文件的内容有很多的时候&…

LiveGBS流媒体平台GB/T28181常见问题-如何快速查看推流上来的摄像头并停止摄像头推流?

LiveGBS流媒体平台GB/T28181常见问题-如何快速查看推流上来的摄像头并停止摄像头推流&#xff1f; 1、负载信息2、负载信息说明3、会话列表查看3.1、会话列表 4、停止会话5、搭建GB28181视频直播平台 1、负载信息 实时展示直播、回放、播放、录像、H265、级联等使用数目 2、负…

Linux下的进程操作

进程概念 ps -elf&#xff1a;查看操作系统的所有进程&#xff08;Linux命令&#xff09; ctrl z&#xff1a;把进程切换到后台 crtl c&#xff1a;结束进程 fg&#xff1a;把进程切换到前台 获取进程进程号和父进程号 函数原型&#xff1a; pid_t getpid(void); //pid_t…

【阻塞队列】阻塞队列的模拟实现及在生产者和消费者模型上的应用

文章目录 &#x1f4c4;前言一. 阻塞队列初了解&#x1f346;1. 什么是阻塞队列&#xff1f;&#x1f345;2. 为什么使用阻塞队列&#xff1f;&#x1f966;3. Java标准库中阻塞队列的实现 二. 阻塞队列的模拟实现&#x1f35a;1. 实现普通队列&#x1f365;2. 实现队列的阻塞功…

美赛注意事项

2024年1月27日 &#xff1a; 赖维杰 同学分享 1、最后的展现必须要漂亮&#xff08;绘图、呈现&#xff09; 李维情 西北建模王 论文位&#xff08;核心&#xff09;必须清楚建模位、编程位知道做了些什么 常见模型&#xff1a; 1、看真题&#xff0c;读往年论文&#xff0c;选…

计算机找不到ucrtbased.dll无法运行程序,分享5种有效的解决方法

当计算机系统在运行过程中无法找到ucrtbased.dll这个特定的动态链接库文件时&#xff0c;可能会引发一系列的问题和故障现象。ucrtbased.dll是Windows操作系统中一个至关重要的组件&#xff0c;它包含了C运行时库的核心函数&#xff0c;对于许多应用程序特别是基于Microsoft Vi…

vue中的computed

目录 一&#xff1a;介绍 二&#xff1a;例子演示 一&#xff1a;介绍 在 Vue.js 中&#xff0c;computed 属性是一种特殊类型的属性&#xff0c;它允许你声明依赖于其他数据属性的值。computed 属性的值是通过一个函数计算得出的&#xff0c;这个函数可以在其依赖的数据发生…

【misc | CTF】攻防世界 适合作为桌面

天命&#xff1a;这题还挺繁琐的&#xff0c;知识点还不少 目录 步骤1&#xff1a;图片隐写 步骤2&#xff1a;Winhex查看ascii码 步骤1&#xff1a;图片隐写 拿到这张图片&#xff0c;不可能扔进ps会有多图层&#xff0c;普通图片也就一个图层而已 但居然可以有隐写图片这…

I/O多路复用

简介&#xff1a; I/O 多路复用(I/O 多路转接)使得程序能同时监听多个文件描述符&#xff0c;能够提高程序的性能&#xff0c;Linux 下实现 I/O 多路复用的系统调用主要有 select 、 poll 和 epoll 。 select &#xff1a; 主旨思想&#xff1a; 1. 首先要构造一个关于文…

查询排序(2)

Oracle从入门到总裁:https://blog.csdn.net/weixin_67859959/article/details/135209645 1.选择部门 30 中的所有员工 SQL> select *2 from emp3 where deptno 30;EMPNO ENAME JOB MGR HIREDATE SAL COMM …

《动手学深度学习(PyTorch版)》笔记2

Chapter2 Preliminaries 2.1 Automatic Differentiation 让计算机实现微分功能&#xff0c; 有以下四种方式&#xff1a; - 手工计算出微分&#xff0c; 然后编码进代码 - 数值微分 (numerical differentiation) - 符号微分 (symbolic differentiation) - 自动微分&#xff0…

搜维尔科技:【简报】元宇宙数字人赛道,《莉思菱娜》

个性有些古灵精怪时儿安静时而吵闹&#xff0c;虽然以人类寿命来算已经200多岁但在 吸血鬼中还只是个小毛头&#xff0c;从中学开始喜欢打扮偏爱黑白灰色系的服装喜欢时 尚圈&#xff0c;立志想成为美妆或时尚网红不过目前还是学生&#xff0c;脸上的浅色血迹是纹身 贴纸&#…

Javat集合之Lis---(ArrayList和LinkedList)

文章目录 一、 List概述1.1概念1.2list体系结构图1.3 通用方法测试代码 二、List的特点三、遍历方式foreachfor循环迭代器 四、ArrayListArrayList概述概念数据结构 ArrayList的特点 ArrayList去重字符串去重对象去重 五、LinkedListLinkedList概述概念数据结构LinkedList的特点…

一键轻松,免费创造:QuickQR带你体验AI二维码的轻松生成!

当今时代&#xff0c;将信息快速转变为可扫描图案&#xff0c;以简化人们的生活和工作方式&#xff0c;二维码技术展现了它强大的功能。特别是在分享链接、联系信息或进行支付时&#xff0c;二维码已成为现代社会一个不可或缺的部分。本文将探讨生成AI二维码的一种工具&#xf…

线性表--队列

1.什么是队列&#xff1f; 队列是只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先 进先出FIFO(First In First Out) &#xff1b; 入队列&#xff1a;进行插入操作的一端称为队尾&#xff1b; 出队列&#xff1a;进行…

幻兽帕鲁服务器搭建,包教包会

服务器搭建 幻兽帕鲁服务器搭建&#xff0c;包教包会&#xff0c;不会评论区评论手把手帮忙搭建 一、steamCMD安装 1、安装screen&#xff1a; yum install screen -y 2、切换用户&#xff1a; su -ls /bin/bash steam 3、切换至steam用户目录&#xff1a; cd ~ 4、下载ste…

如何在docker容器中安装Elasticsearch中的IK分词器

目录 &#xff08;1&#xff09;准备IK分词器的压缩包 &#xff08;2&#xff09;进入docker容器 &#xff08;3&#xff09;移动ik分词器到指定文件夹 &#xff08;4&#xff09;解压分词器压缩包 &#xff08;5&#xff09;测试IK分词器是否安装成功 &#xff08;1&#…