当前位置: 首页 > news >正文

代码随想录打卡|Day29 动态规划Part02(不同路径、不同路径2、整数拆分、不同的二叉树搜索)

动态规划Part02

不同路径

力扣题目链接
代码随想录链接
视频讲解链接

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

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

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

在这里插入图片描述

思路:动态规划五步:
在这里插入图片描述

  1. dp[m][n]含义:从(0,0)位置移动到(m,n)位置的所有路径
  2. 递推公式:由于只能向下或者向右走,因此到达(m,n)的两个出口只可能是(m-1,n)和(m,n-1),因此,到达(m-1,n)和(m,n-1)的路径总和就是到达(m,n)的路径总和。
  3. 初始化:由于只能向右和向下走,所以当行号(i)为0的时候,无论列如何变化dp[0][j]的值始终只能为1。同理可得dp[i][0]的值也是始终只能为1.
  4. 遍历顺序,行和列均从顺序2开始。
  5. 打印数组。
    代码如下:
class Solution {public int uniquePaths(int m, int n) {// dp[m][n]代表从(0,0)到(m,n)一共有多少条路径int[][] dp = new int[m][n];// 初始化dp数组,由于每次只能向下或者向右走// 所以当j为0的时候,从(0,0)到(i,0)只有一种走法for(int i = 0 ; i < m ; i++) dp[i][0] = 1;// 当i为1的时候,从(0,0)到(0,j)也只有一种走法for(int j = 0 ; j < n ; j++) dp[0][j] = 1;// 对于走到(i,j)的位置,当我们有m种方法走到(i-1,j)和n种方法走到(i,j-1)的时候// 走到(i,j)位置就有m+n种方法。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];}}return dp[m - 1][n - 1];}
}

不同路径 II

力扣题目链接
代码随想录链接
视频讲解链接

题目描述: 给定一个 m x n 的整数数组 grid。一个机器人初始位于 左上角(即 grid[0][0])。机器人尝试移动到 右下角(即 grid[m - 1][n - 1])。机器人每次只能向下或者向右移动一步。

网格中的障碍物和空位置分别用 1 和 0 来表示。机器人的移动路径中不能包含 任何 有障碍物的方格。

返回机器人能够到达右下角的不同路径数量。

测试用例保证答案小于等于 2 * 109。
在这里插入图片描述

在这里插入图片描述

动态规划五步策略:

  1. dp[i][j]:(0,0)到(i,j)所有的可行路径总数。
  2. 递推公式:当dp[i][j]的左入口(dp[i][j-1])和上入口(dp[i-1][j])的值均不为-1的时候,dp[i][j] = dp[i-1][j] + dp[i][j-1];当左为-1,上不为-1的时候,dp[i][j] = dp[i-1][j]。当上不为-1,左为-1的时候,dp[i][j] = dp[i-1][j],当上左均为-1的时候dp[i][j] =-1。
  3. 初始化:由于只能向下或者向右走,所以当行号和列号为0的时候,我们对其赋值为1,遇到障碍物,障碍物所在格子和他的后面的格子的值均赋值为-1,代表不可通行。
  4. 遍历顺序:正常行列遍历
  5. 打印数组

代码如下:

class Solution {public int uniquePathsWithObstacles(int[][] obstacleGrid) {int m = obstacleGrid.length;int n = obstacleGrid[0].length;int[][] dp = new int[m][n];// 初始化dp数组// 初始化列为0的时候行的值为1for(int i = 0 ; i < m ; i++){// 当行上出现障碍物之后,将该位置朝后的所有行值均赋值为-1,代表不可通行。if(obstacleGrid[i][0] == 1){dp[i][0] = -1;          if(i < m - 1)obstacleGrid[i + 1][0] = 1;}          else{dp[i][0] = 1;}}// 初始化行为0的时候列的值为1for(int j = 0 ; j < n ; j++){// 当列上出现障碍物之后,将该位置朝后的所有列值均赋值为-1,代表不可通行。if(obstacleGrid[0][j] == 1){dp[0][j] = -1;if(j < n - 1)obstacleGrid[0][j + 1] = 1;}else{dp[0][j] = 1;}}for(int i = 1 ; i < m ; i++){for(int j = 1 ; j < n ; j++){// 若不是第一行或者第一列的值为1,则将当前障碍物所在位置赋值为-1;if(obstacleGrid[i][j] == 1){dp[i][j] = -1;continue;}/**1.当dp[i - 1][j] == -1 && dp[i][j - 1] != -1,代表(i,j)位置只能从(i,j-1)处抵达。2.当dp[i - 1][j] != -1 && dp[i][j - 1] == -1,代表(i,j)位置只能从(i-1,j)处抵达。3.当dp[i - 1][j] != -1 && dp[i][j - 1] != -1,代表(i,j)位置可以从(i-1,j)和(i,j-i)抵达。4.当dp[i - 1][j] == -1 && dp[i][j - 1] == -1,代表(i,j)位置不可抵达,因此dp[i][j] = -1;*/if(dp[i - 1][j] == -1 && dp[i][j - 1] != -1){dp[i][j] = dp[i][j - 1];}else if(dp[i - 1][j] != -1 && dp[i][j - 1] == -1){dp[i][j] = dp[i - 1][j];}else if(dp[i - 1][j] != -1 && dp[i][j - 1] != -1){dp[i][j] = dp[i - 1][j] + dp[i][j - 1];}else{dp[i][j] = -1;}}}// 若dp[m - 1][n - 1] == -1,则代表(m,n)处不可抵达。所以路径数量为0;if(dp[m - 1][n - 1] == -1)return 0;return dp[m - 1][n - 1];}
}

代码随想录版本

class Solution {public int uniquePathsWithObstacles(int[][] obstacleGrid) {int m = obstacleGrid.length;int n = obstacleGrid[0].length;int[][] dp = new int[m][n];//如果在起点或终点出现了障碍,直接返回0if (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];}
}

优化空间版本

// 空间优化版本
class Solution {public int uniquePathsWithObstacles(int[][] obstacleGrid) {int m = obstacleGrid.length;int n = obstacleGrid[0].length;int[] dp = new int[n];for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) {dp[j] = 1;}for (int i = 1; i < m; i++) {for (int j = 0; j < n; j++) {if (obstacleGrid[i][j] == 1) {dp[j] = 0;} else if (j != 0) {dp[j] += dp[j - 1];}}}return dp[n - 1];}
}

整数拆分(需要多理解一下)

力扣题目链接
代码随想录链接
视频讲解链接

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

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

动态规划:

代码如下:

class Solution {public int integerBreak(int n) {//dp[i] 为正整数 i 拆分后的结果的最大乘积int[] dp = new int[n+1];dp[2] = 1;for(int i = 3; i <= n; i++) {for(int j = 1; j <= i-j; j++) {// 这里的 j 其实最大值为 i-j,再大只不过是重复而已,//并且,在本题中,我们分析 dp[0], dp[1]都是无意义的,//j 最大到 i-j,就不会用到 dp[0]与dp[1]dp[i] = Math.max(dp[i], Math.max(j*(i-j), j*dp[i-j]));// j * (i - j) 是单纯的把整数 i 拆分为两个数 也就是 i,i-j ,再相乘//而j * dp[i - j]是将 i 拆分成两个以及两个以上的个数,再相乘。}}return dp[n];}
}

不同的二叉搜索树(详细讲解需要看代码随想录)

力扣题目链接
代码随想录链接
视频讲解链接

题目描述: 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
在这里插入图片描述

思路:总的来说,我们需要轮流让每个节点充当root节点,每个节点当root节点的时候,其二叉搜索树额度变换形式只会由左右子树的形式所决定,即 :左子树形式数量X右子树形式数量。最后将所有节点的形式数量和相加即可得到当前值的二叉搜索树的形式数量。

代码如下:

class Solution {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 = 1 ; j <= i ; j++){dp[i] += dp[j - 1] * dp[i-j];}}return dp[n];}
}

相关文章:

  • 免费LUT网站
  • 【Docker】使用 jq 管理镜像源
  • C++核心编程:类与对象全面解析
  • uniapp常用
  • 迭代器与生成器
  • 2025A卷-正整数到Excel编号之间的转换
  • 什么是 Web 标准?为什么它们对 SEO 和开发很重要?
  • GitLab CVE-2024-12444 安全漏洞解决方案
  • Vue+Echarts 3D地图效果
  • Java锁的升级流程详解:无锁、偏向锁、轻量级锁、重量级锁
  • YUM/DNF管理工具
  • 用vite动态导入vue的路由配置
  • 递归、搜索和回溯算法《递归》
  • 飞凌嵌入式T527核心板获得【OpenHarmony生态产品兼容性证书】
  • window 图形显示驱动-在 WDDM 1.2 中提供无缝状态转换(下)
  • 关于健身房管理系统前后端软件开发主要功能需求分析
  • 《Astro 3.0岛屿架构让内容网站“脱胎换骨”》
  • RISCV学习(5)GD32VF103 MCU架构了解
  • 【AI News | 20250428】每日AI进展
  • transformer-实现单层encoder_layer
  • 国家发改委答澎湃:力争6月底前下达2025年两重建设和中央预算内投资全部项目清单
  • 在上海生活8年,13岁英国女孩把城市记忆写进歌里
  • 一回合摘下“狮心”,张名扬霸气回应观众:再嘘一个我听听
  • 六朝文物草连空——丹阳句容南朝石刻考察纪
  • 三大交易所修订股票上市规则:明确关键少数责任,强化中小股东保障
  • 明查|把太平洋垃圾污染问题甩锅中国,特朗普用的是P过的图