代码随想录算法训练营第29天 | 第九章动态规划 part02

news/2024/9/29 1:10:15/

第九章 动态规划 Part 02

文章目录

  • 第九章 动态规划 Part 02
    • 详细布置
      • 62. 不同路径
      • 63. 不同路径 II
      • 343. 整数拆分(可跳过)
      • 96. 不同的二叉搜索树(可跳过)

今天开始逐渐有了动态规划的感觉了。前两题 不同路径非常适合进阶,大家可以好好研究一下。


详细布置

62. 不同路径

这道题掌握动态规划的方法就可以了。虽然数论方法也能解决问题,但有些非主流且很难想到。

题目链接
核心思想:dp[i][j] = dp[i - 1][j] + dp[i][j - 1],因为dp[i][j]只有这两个方向过来。
视频讲解:B站视频链接
代码和思路还是比较好理解的,但是最大的问题还是

class Solution {
public:int uniquePaths(int m, int n) {vector<vector<int>> dp(m, vector<int>(n, 0));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 - 1][j]+dp[i][j-1];}}return dp[m-1][n-1];}
};

使用一维数组去解决,思路也非常巧妙,相当于,先确定第一列,然后dp[i] += dp[i - 1];即dp[i] =dp[i]+ dp[i - 1];。其实dp[i]相当于dp[i][j-1],相当于左边哪一个,dp[i - 1]相当于dp[i - 1][j],相当于上一行的。思路非常巧妙。

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

二叉树的方法,没想到。根节点:代表起始位置。左子节点:代表向下移动一步。右子节点:代表向右移动一步。叶节点:代表到达终点的所有可能位置。
对于一个 m x n 的网格,二叉树的高度为 m + n - 1(因为需要 m-1 次向下移动和 n-1 次向右移动),并且树的宽度(在任意层的最大节点数)将从 1 增加到 m+n-1。二叉树
在这里插入图片描述

class Solution {
private:int dfs(int i, int j, int m, int n) {if (i > m || j > n) return 0; // 越界了if (i == m && j == n) return 1; // 找到一种方法,相当于找到了叶子节点return dfs(i + 1, j, m, n) + dfs(i, j + 1, m, n);}
public:int uniquePaths(int m, int n) {return dfs(1, 1, m, n);}
};

63. 不同路径 II

题目链接
视频讲解:B站视频链接
在这里插入图片描述
这题核心思想就是,如果有障碍,就把当前位置dp值置0。思路很简单,但是写代码时很容易出问题,首先,我直接dp=obstacleGrid()。方便快捷,后来发现不行,因为空路径是0,有物体是1,虽然也能写,但是思路不清晰,容易被绕着。还是得重新定义一个

class Solution {
public:int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {int m = obstacleGrid.size();int n = obstacleGrid[0].size();vector<vector<int>> dp(m, vector<int>(n, 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++) {if(obstacleGrid[i][j]==1)continue;dp[i][j] = dp[i - 1][j]+dp[i][j-1];}}return dp[m-1][n-1];}
};

343. 整数拆分(可跳过)

这道题的思路并不容易想到,建议在第一遍学习时可以跳过。如果你学有余力,推荐看视频理解一下。

class Solution {
public:int integerBreak(int n) {vector<int> dp(n + 1);dp[2] = 1;for(int i=3;i<=n;i++)for (int j = 1; j <= i / 2; j++) {dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));}return dp[n];}
};

这题看着挺难的,结果发现居然是这么简单。就是层层迭代,然后层层循环,找出和之前的最大值。一层层的找 dp[i - j] * j的最大值,不断的遍历查值。思路还是比较清晰。 想明白了这个就可以:dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
题目链接
视频讲解:B站视频链接

贪心算法也很好理解,其实感觉自己最开始的想法就是贪心算法,4>2*2, m<(m-2)*2,
选择 3 作为最优拆分因子而不是 2 是因为通过数学推导可以证明,但是我也看不太懂。不太理解。

class Solution {
public:int integerBreak(int n) {if (n == 2) return 1;if (n == 3) return 2;if (n == 4) return 4;int result = 1;while (n > 4) {result *= 3;n -= 3;}result *= n;return result;}
};

知道这个,代码就好写了呢。


96. 不同的二叉搜索树(可跳过)

本题的思路也比较复杂,建议在一刷时跳过。如果你学有余力,推荐看视频理解。
在这里插入图片描述
在这里插入图片描述
重点是看上面的图,把顺序推一遍
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[4] = dp[3] * dp[0] + dp[2] * dp[1] + dp[1] * dp[2]+ + dp[0] * dp[3].如此,代码就很好写了

class Solution {
public:int numTrees(int n) {vector<int>nums(n+1,0);nums[0]=1;nums[1]=1;for(int i=2;i<=n;i++)for(int j=0;j<i;j++)nums[i]+= nums[j]*nums[i-j-1];return nums[n];}
};

题目链接
视频讲解:B站视频链接


通过这几道题,我们逐渐加深对动态规划的理解。接下来会继续学习更复杂的动态规划问题。


http://www.ppmy.cn/news/1530838.html

相关文章

Gnu Radio抓取WiFi信号,流程图中模块功能

模块流程如图所示&#xff1a; GNURadio中抓取WiFi信号的流程图中各个模块的功能&#xff1a; UHD: USRP Source&#xff1a; 使用此模块配置USRP硬件进行信号采集。设置频率、增益、采样率等参数。Complex to Mag^2&#xff1a; 将复数IQ数据转换为幅度的平方。Delay&#xf…

OpenCV第九章——图形检测

1.图像的轮廓 轮廓是指图形或物体的外边缘线条&#xff0c;简单的几何图形是由平滑的线构成的容易识别&#xff0c;但不规则图形的轮廓由许多个点构成&#xff0c;识别起来比较困难。Opencv提供了findContours()方法来判断图像的边缘&#xff0c;之后将边缘的点封装成数…

神经网络构建原理(以MINIST为例)

神经网络构建原理(以MINIST为例) 在 MNIST 手写数字识别任务中&#xff0c;构建神经网络并训练模型来进行分类是经典的深度学习应用。MNIST 数据集包含 28x28 像素的手写数字图像&#xff08;0-9&#xff09;&#xff0c;任务是构建一个神经网络&#xff0c;能够根据输入的图像…

如何使用numpy反转数组

如何使用numpy反转数组 1、使用np.flip()函数 可以使用flip(m, axisNone)函数来对数组进行反转&#xff1a; m:输入数组 axis:为None则行列都反转 axis:为0则反转行 axis:为1则反转列2、代码 import numpy as np# 创建一维数组 arr np.array([[1, 2, 3, 4, 5],[2, 2, 3, 4…

深度学习(6):Dataset 和 DataLoader

文章目录 Dataset 类DataLoader 类 Dataset 类 概念&#xff1a; Dataset 是一个抽象类&#xff0c;用于表示数据集。它定义了如何获取数据集中的单个样本和标签。 作用&#xff1a; 为数据集提供统一的接口&#xff0c;便于数据的读取、预处理和管理。 关键方法&#xff…

Linux部署python web项目Flask + gunicorn + nginx

文章目录 一、安装python&使用虚拟环境二、python程序重要参数加密2.1 非对称加密&#xff08;RSA&#xff09;2.2 生成密钥对2.4 以连接数据库参数加密为例2.4.1 工具类RSA.py 三、一个简单的Flask项目四、安装配置gunicorn4.1 安装4.2 启动/配置(选择eventlet)4.2.1 命令…

网络爬虫到底难在哪里?

如果你是自己做爬虫脚本开发&#xff0c;那确实难&#xff0c;因为你需要掌握Python、HTML、JS、xpath、database等技术&#xff0c;而且还要处理反爬、动态网页、逆向等情况&#xff0c;不然压根不知道怎么去写代码&#xff0c;这些技术和经验储备起码得要个三五年。 比如这几…

中国蚁剑(antSword)安装使用

antSword下载 antSword-Loader下载 作者&#xff1a;程序那点事儿 日期&#xff1a;2024/09/12 19:35 中国蚁剑&#xff08;AntSword&#xff09;是一款跨平台的开源网站管理工具&#xff0c;旨在满足渗透测试人员的需求。它是一个功能强大的工具&#xff0c;可以帮助用户管理…