动态规划算法题刷题笔记

news2024/11/19 5:06:00

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

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

最优子结构:

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

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

  • 确定 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.coloradmin.cn/o/1417254.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Redis】Redis有哪些适合的场景

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Redis ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 &#xff08;1&#xff09;会话缓存&#xff08;Session Cache&#xff09; &#xff08;2&#xff09;全页缓存&#xff08;FPC…

JetpackCompose 之 状态学习

1.无状态组件 1.1导入依赖 implementation("androidx.core:core-ktx:1.9.0")implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")implementation("androidx.activity:activity-compose:1.7.0")implementation(platform("an…

MyBatis详解(3)-- 动态代理及映射器

MyBatis详解&#xff08;3&#xff09; mybatis 动态代理动态代理的规范selectOne和selectListnamespace mybatis映射器映射器的引入&#xff1a; 映射器的组成select 元素结构&#xff1a;单个参数传递多个参数传递 insert 元素结构主键回填&#xff1a;自定义主键生成规则 u …

CSS 双色拼接按钮效果

<template><view class="sss"><button> <!-- 按钮 --><view class="span"> 按钮 </view> <!-- 按钮文本 --></button></view></template><script></script><style>body {b…

JUC-ReentrantLock,ReentrantReadWriteLock,StampedLock

1. 概述 前面介绍过了synchronized关键字作用的锁升级过程 无锁->偏向锁->轻量锁->重锁 下面再介绍实现Lock接口的锁的升级过程 无锁->独占锁&#xff08;ReentrantLock&#xff0c;Synchronized&#xff09;->读写锁(ReentranReadWriteLock)->邮戳锁(Stamp…

app逆向-apktool逆向工具安装使用

文章目录 一、前言二、安装三、执行 一、前言 apktool是一个能够解析和分析安卓应用文件&#xff08;APK&#xff09;的工具&#xff0c;帮助了解应用程序的内部结构和功能。 二、安装 window版本 下载地址&#xff1a;版本2.9.3&#xff0c;并将下载的 jar 重命名为apktoo…

【缓存周总结】Redis缓存的使用以及数据安全的处理

前言 Redis非关系型数据库已经是很常见的工具了&#xff0c;项目中用到的也很多&#xff0c;这篇文章系统的分析下使用过程中可能会遇到的问题 一、缓存 缓存是数据交换的缓冲区&#xff0c;是存贮数据的临时地方&#xff0c;一般读写性能较高。 我们项目中引用的Redis目的就是…

SPI:JDK 与 SpringBoot

浅谈Java SPI原理与其在JDBC、Flink中的应用 API&#xff1a;由被调方提供的实现了某个完整功能的接口&#xff0c;主调方直接调用该接口来享用该功能&#xff0c;而无需关注该接口的具体实现。比如使用 JDK 的 InputStream#read 从文件系统中读取数据。 SPI&#xff1a;被调方…

DOM 型 XSS 攻击演示(附链接)

一、介绍 DOM&#xff08;Document Object Model&#xff09;型 XSS&#xff08;Cross-Site Scripting&#xff09;攻击是一种 Web 应用程序中的安全漏洞&#xff0c;其特点是攻击者成功地注入了恶意脚本&#xff0c;这些脚本在用户的浏览器中执行&#xff0c;从而导致恶意行为…

DETR解读,将Transformer带入CV

论文出处 [2005.12872] End-to-End Object Detection with Transformers (arxiv.org) 一个前置知识 匈牙利算法&#xff1a;来源于二部图匹配&#xff0c;计算最小或最大匹配 算法操作&#xff1a;在n*n的矩阵中 减去行列最小值&#xff0c;更新矩阵&#xff08;此时行或者…

C# RichTextBox常用属性、方法学习1

1 字体 Font font1 new Font("宋体", 18); richTextBox1.Font font1; Font font2 new Font("宋体", 10, FontStyle.Underline); richTextBox1.SelectionFont font2; 定义字体&#xff0c;可以带2个参数&#…

代码随想录刷题笔记-Day13

1. 二叉树的层序遍历 102. 二叉树的层序遍历https://leetcode.cn/problems/binary-tree-level-order-traversal/层次遍历依靠队列的先进先出特点实现。 解题思路 层序遍历的本质就是对每一个pop出来的处理节点&#xff0c;处理后把他的左右节点放进去。 对于每一层来说&…

菱形打印和十进制ip转二进制

1.菱形打印 用for循环 #!/bin/bashread -p "请输入菱形的大小&#xff1a;" num #打印向上的等腰三角形 for ((i1;i<num;i)) dofor ((jnum-1;j>i;j--))doecho -n " " #打印的是前面的空格donefor ((k1;k<2*i-1;k))doecho -n "*" #打印…

【stm32】hal库学习笔记-FSMC连接TFT_LCD

【stm32】hal库学习笔记-FSMC连接TFT LCD 触摸屏结构与原理 LCD模块接口原理图 LCD 接口连接在 FSMC 总线上面&#xff0c;图中的 T_MISO/T_MOSI/T_PEN/T_SCK/T_CS 连接在 MCU 的 PB2/PF11/PB1/PB0/PC13 上&#xff0c;这些信号用来实现对液晶触摸屏的控制&#xff08;支持电阻…

QT tcp与udp网络通信以及定时器的使用 (7)

QT tcp与udp网络通信以及定时器的使用 文章目录 QT tcp与udp网络通信以及定时器的使用1、QT网络与通信简单介绍2、QT TCP通信1、 服务器的流程2、 客户端的流程3、服务器的编写4、客户端的编写 3、QT UDP通信1、客户端流程2、客户端编写3、UDP广播4、UDP组播 4、定时器的用法1、…

leetcode刷题日志-146LRU缓存

思路&#xff1a;使用hashmap储存key&#xff0c;vaule&#xff0c;使用双向链表以快速查到尾结点&#xff08;待逐出的节点&#xff09;&#xff0c;链表的题一定要在纸上画一下&#xff0c;不然连着连着就不知道连在哪里去了 class LRUCache {public class ListNode {int ke…

【js基础】日期对象的使用,查找、增加、克隆、删除DOM节点,M端事件

文章目录 前言一、日期对象日期对象的作用1.1 实例化1.2 日期对象的方法1.3 时间的格式化1.4 时间戳的使用时间戳是什么js的时间戳 二、DOM的增删改查什么叫做DOM节点2.1 DOM的查找2.2 增加节点2.3 克隆节点和删除节点 三、M端事件3.1 M端是什么&#xff1f; 总结 前言 在 Jav…

你的MiniFilter安全吗?

简介 筛选器管理器 (FltMgr.sys)是Windows系统提供的内核模式驱动程序, 用于实现和公开文件系统筛选器驱动程序中通常所需的功能; 第三方文件系统筛选器开发人员可以使用FltMgr的功能可以更加简单的编写文件过滤驱动, 这种驱动我们通常称为MiniFilter, 下面是MiniFilter的基本…

STM32CubeMX教程31 USB_DEVICE - HID外设_模拟键盘或鼠标

目录 1、准备材料 2、实验目标 3、模拟鼠标实验流程 3.0、前提知识 3.1、CubeMX相关配置 3.1.0、工程基本配置 3.1.1、时钟树配置 3.1.2、外设参数配置 3.1.3、外设中断配置 3.2、生成代码 3.2.0、配置Project Manager页面 3.2.1、设初始化调用流程 3.2.2、外设中…

优选6款前端动画特效分享(附在线演示)

优选6款前端动画特效 其中有CSS动画、canvas动画、js小游戏等等 下方效果图可能不是特别的生动 那么你可以点击在线预览进行查看相应的动画特效 同时也是可以下载该资源的 翻页时钟特效 基于react实现的一款翻页时钟特效 切换时间特效跟比赛切换分数牌相似 以下效果图只能体现…