【刷题笔记】动态规划

动态规划刷题笔记(基础题)

  • ① 打家劫舍
  • ② 删除并获得点数
  • ③ 不同路径
  • ④ 最小路径和
  • ⑤ 不同路径Ⅱ
  • ⑥ 统计全为 1 的正方形子矩阵
  • ⑦ 最大正方形

① 打家劫舍

//不在栈内申请内存,防止溢出
int dp[101] = {0};
class Solution {
public:int rob(vector<int>& nums) {int sz = nums.size();//处理特殊情况if(sz == 1) return nums[0];else if(sz == 2) return max(nums[0], nums[1]);//先定义好前两个屋子,因为如果只有一个屋,答案就是它,如果两个屋,答案是比较大那个dp[0] = nums[0]; dp[1] = max(nums[0], nums[1]); for(int i = 2; i < sz; ++i) // sz = 3{//注释的部分是我第一遍写的代码,也确实是通过了全部用例,但比题解要复杂一些//这里通过遍历除了当前屋子紧挨的那屋前面所有dp状态找到能偷的最大值//但其实只用找dp[i - 2]就好了,所以多遍历了很多地方// int max = 0;// for(int j = 0 ; j < i - 1; ++j)// {//     if(max < arr[j]) max = arr[j];// }// arr[i] = nums[i] + max;//两个状态,要么不偷i这个屋子,偷的钱就是dp[i - 1]//要么就是偷i这个屋子,偷的钱就是i的钱数+dp[i - 2]dp[i] = max(nums[i] + dp[i - 2], dp[i - 1]);}//最大值出现在dp数组最后,直接返回即可return dp[sz - 1];}
};

② 删除并获得点数

仔细看完题目之后会觉得和打家劫舍非常相似,但是这里我们为了方便取出要利用到哈希算法,具体实现请看代码:

class Solution {
public:int deleteAndEarn(vector<int>& nums) {//处理特殊情况if(nums.empty()) return 0;else if(nums.size() == 1) return nums[0];int sz = nums.size();//找到整个数组中的最大值,以便创建哈希数组。int nums_max = nums[0];for(int i = 1; i < sz; ++i){nums_max = nums[i] > nums_max ? nums[i] : nums_max;}//向哈希数组中存储对应的个数,有一个就在对应位置加一vector<int> count(nums_max + 1);for(int i = 0; i < sz; ++i){count[nums[i]] += 1;}//最后这段是核心,也是理解动态规划的重点//题目中要求如果你删除掉了一个数获得了其积分,那么其紧邻的所有数都要被删除(没有积分)//因此在每次删除中你要么选择删除其紧邻的前一个数而放弃掉i所在位置的数//要么获得i所在数的全部积分以及i的上上个状态下的dp状态积分//也就是下面循环中单独那个语句的含义vector<int> dp(nums_max + 1);dp[1] = count[1] * 1;for(int i = 2; i <= nums_max; ++i){dp[i] = max(dp[i - 1],dp[i - 2] + i * count[i]);}//由于dp[0]不存东西,因此nums_max的位置是dp数组最后一位,也就是最大积分//(因为每一步都在寻找最大积分)return dp[nums_max];}
};

③ 不同路径

动态规划方程是每一个状态的路径数=其上面一个格子和左面一个格子的路径数之和,看代码👇

// 申请dp数组
int dp [110][110] = { 0 };
class Solution {
public:int uniquePaths(int m, int n) {for(int i = 1; i <= m; ++i){for(int j = 1; j <= n; ++j){// 初始化第一个格子if(i == 1 && i == j){dp[i][j] = 1;continue;} // 否则将当前格子上面和左面的格子的数字加起来就是结果dp[i][j] = dp[i-1][j] + dp[i][j-1];}}//返回dp数组最后一个位置的值(因为走到最后一个格子)return dp[m][n];}
};

④ 最小路径和

首先仍然只能往右走和往下走,然后给了个路径矩阵,让你找一个路径,使路径上数字和最小,代码👇

int dp[210][210] = {0};
class Solution {
public:int minPathSum(vector<vector<int>>& grid) {int row = grid.size();int col = grid[0].size();dp[1][1] = grid[0][0];for(int i = 1; i <= row; ++i){for(int j = 1; j <= col; ++j){//跳过第一格if(i == 1 && j == i) continue;//对于第一行的元素,除了首个元素,都只能从其左边右移得到//因此dp数组的当前状态应该是其左边的dp状态加上当前位置的值//这里grid都-1了是因为我是从i=1,j=1开始遍历的,对应grid中的位置要-1if(i == 1)dp[i][j] = grid[i - 1][j - 1] + dp[i][j-1];//对于第一列的元素,除了首个元素,都只能从其上面下移得到//因此dp数组的当前状态应该是其上面边的dp状态加上当前位置的值else if(j == 1)dp[i][j] = grid[i - 1][j - 1] + dp[i-1][j];//如果不在边上,那么就是上面格子和左面格子找个小的加上当前grid对应位置的值elsedp[i][j] = grid[i - 1][j - 1] + min(dp[i-1][j],dp[i][j-1]);}}//返回dp数组右下角元素return dp[row][col];}
};

⑤ 不同路径Ⅱ

和不同路径的区别就是多了个障碍,遇到障碍要绕开。

class Solution {
public:int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {int row = obstacleGrid.size();int col = obstacleGrid[0].size();vector<int> dp (col);//如果第一个位置就是障碍,直接就是0dp[0] = (obstacleGrid[0][0] == 0);for(int i = 0; i < row; ++i){for(int j = 0; j < col; ++j){if(obstacleGrid[i][j] == 1){//如果当前位置障碍是1,那么dp数组对应位置就是0dp[j] = 0;continue;}//若当前位置不是第一列并且当前位置无障碍,就根据其左侧和上侧状态求出dp[j]//而dp[j-1]就是(i,j)位置的左侧,被+=的dp[j]就是(i,j)位置的上侧//最后的+=结果就是(i,j)位置的新dp结果if(j > 0 && obstacleGrid[i][j] == 0)dp[j] += dp[j - 1];}}return dp.back();}
};

不理解滚动数组看看这里
在这里插入图片描述
这里面涉及到一个滚动数组的概念。正常我们要设计二维dp来解决问题,但在此题目中,新路径数等于其dp矩阵中上面和左面的数字之和,如果换成一维数组,其左边的dp[j - 1]是刚好更新过的数字,那上面呢?上面是马上就要被覆盖的数字,就是+=之前的dp[j]!所以新的dp[j]就是原来的dp[j]加上刚刚左边更新的dp[j - 1],就可以完成当前格子的更新。

⑥ 统计全为 1 的正方形子矩阵

此题重点仍然是理解dp推导的形式:
首先,dp矩阵的第一行和第一列直接copy原矩阵,是0就是0,是1就是1,因为在边上没办法组成边长大于一的正方形;
其次,除第一行和第一列外,如果矩阵中某个位置是0,那么与其对应的dp矩阵的相同位置也是0,因为包含此位置的情况是肯定组不成正方形的;
最后,也是最难理解的一点,假设dp数组dp[i][j]表示的是以(i, j)这个坐标为正方形右下角时正方形的最大边长,那么就可以认为最终的答案就是dp[i][j]这个二维dp数组中所有数之和(这里跳了很多步骤,讲解一下。因为每个正方形只有一个右下角格子,如果二维dp数组中存储的是以当前坐标格作为正方形右下角而算出来的能够形成正方形的个数,那么dp数组中每个格子所计算的个数都是不重复的[都有各自的右下角],最后把他们加在一起,就能得到能形成的正方形的总数。至于为什么存储最大边长就能算出来个数,是因为假设dp[i][j] = 4,那么(i, j)这个坐标作为右下角就能够形成边长分别为1、2、3和4的四个正方形,不难看出,最大边长是几,就能以此坐标为右下角形成几个正方形,自然而然也就可以存入dp矩阵进而求和了。

随后我们来进行递推:
假设包围(i, j)这个坐标的三个坐标(i - 1, j - 1)、(i, j - 1)、(i - 1, j)对应的f[i - 1][j - 1]、f[i][j - 1]、f[i - 1][j]中的最小值3,含义就是这三个坐标均可作为一个最大变长为3的正方形的右下角。前面我们已经说到了,如果矩阵中某个位置是0,那么与其对应的dp矩阵的相同位置也是0,所以如果(i, j)坐标的矩阵值是1,我们就不难发现,f[i][j]只能是4。我们用这个图举例子:
在这里插入图片描述

如果我们知道包围(i, j)这个坐标的三个坐标(i - 1, j - 1)、(i, j - 1)、(i - 1, j)对应的f[i - 1][j - 1]、f[i][j - 1]、f[i - 1][j]中的最小值3(红黄框为4,蓝框为3),那么加上(i, j)这个值为1坐标,我们只能得到边长为4的正方形,想小一点的话,这个4×4矩阵除了右下角全是1,除非这位置是0,不然就只能乖乖形成最大边长为4的右下角;想大一点的话,我们知道f[i - 1][j - 1]、f[i][j - 1]、f[i - 1][j]中的最小值3,你但凡在左侧和上侧各填一行,最少会添一个0,就不成立了。因此直接锁死递推式:

dp[i][j]=min(dp[i][j−1],dp[i−1][j],dp[i−1][j−1])+1 //伪代码,min函数最多接受两个参数

力扣给出的完整递推式如下:
在这里插入图片描述
也是严格按照前面的步骤来的,第一行或者第一列直接赋值;原矩阵为0的位置,dp矩阵也为0;剩下的情况就是求左、左上、上的最小值再加1,就是当前坐标dp矩阵的结果。下面是实现代码👇

class Solution {
public:int countSquares(vector<vector<int>>& matrix) {int ret = 0;int row = matrix.size(), col = matrix[0].size();vector<vector<int>> dp(row, vector<int>(col,0));for(int i = 0; i < row; ++i){for(int j = 0; j < col; ++j){if(i == 0 || j == 0)dp[i][j] = matrix[i][j];else if(matrix[i][j] == 0)dp[i][j] = 0;elsedp[i][j] = min(min(dp[i - 1][j - 1],dp[i - 1][j]),dp[i][j - 1]) + 1;// 在循环最内层把dp矩阵中每一个元素进行累加,以求得结果ret += dp[i][j];}}return ret;}
};

⑦ 最大正方形

和上面的题思路完全一致,这次不是找到所有正方形了,就是单纯找到dp矩阵中的最大值,代码如👇:

class Solution {
public:int maximalSquare(vector<vector<char>>& matrix) {int length = 0;int row = matrix.size(), col = matrix[0].size();int pre = 0;//vector<vector<int>> dp(row, vector<int>(col, 0));vector<int> dp(col + 1);for(int i = 0; i < row; ++i){for(int j = 0; j < col; ++j){int temp = dp[j];if(i == 0 || j == 0)dp[j] = matrix[i][j] - '0';else if(matrix[i][j] == '0')dp[j] = 0;elsedp[j] = min(min(dp[j - 1],dp[j]),pre) + 1;pre = temp;length = length > dp[j] ? length : dp[j];}}return length * length;}
};

这里我们优化了空间,前面一道题的解法使用的是二维的dp矩阵,但是我们每部运算只需要当前位置左、上以及左上三个位置的数据。当我们在使用一维dp数组时,当前位置左侧的就是我们要的左侧,当前位置未更新前的数据就是我们要的上侧,所以我们只需要保存上一次计算中的左上的数据即可,因此引入pre和temp变量,每次pre保存的是这个循环计算前dp[j]的值,留用下次循环进行比较用,而temp则是直接保存计算dp值前的状态,在计算后赋给pre。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习笔记 - 用于3D物体检测的KITTI数据集的使用及说明

一、什么是 KITTI 数据集&#xff1f; KITTI 是由卡尔斯鲁厄理工学院和芝加哥丰田理工学院开发的自动驾驶数据集&#xff08;目前分2012和2015版本&#xff09;。它是计算机视觉研究中使用的图像和 LIDAR 数据的集合&#xff0c;例如立体视觉、光流、视觉里程计、3D 对象检测和…

【Ubuntu-20.04】OpenCV-3.4.16的安装并对图片与视频处理

【Ubuntu-20.04】OpenCV-3.4.16的安装并对图片与视频处理 一、安装OpenCV-3.4.161.下载OpenCV-3.4.16安装包2.将安装包放到/home&#xff0c;并解压3.使用 cmake 安装 opencv4.配置环境5.查看 opencv 的版本信息 二、处理图片&#xff08;一&#xff09;创建文件夹 code &#…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Toggle)

组件提供勾选框样式、状态按钮样式及开关样式。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 仅当ToggleType为Button时可包含子组件。 接口 Toggle(options: { type: ToggleType, is…

【ARM】MDK在programming algorithm界面添加FLM

【更多软件使用问题请点击亿道电子官方网站查询】 1、 文档目标 解决在programming algorithm界面中无法添加想要的Flash编程算法的问题 2、 问题场景 在对于Debug进行Flash Download进行配置的时候&#xff0c;在programming algorithm界面中有对应的Flash编程算法。可以通过…

C++初阶:内存管理

目录 1. C/C中各种资源的内存分布1.1 C/C程序内存区域划分1.2 各资源的内存分布情况&#xff08;练习&#xff09; 2. C中的动态内存管理方式2.1 new/delete开辟内置类型空间2.2 new/delete开辟销毁自定义类型空间 3. operator new 与 operator delete函数4. new与delete的实现…

环信ChatroomUIKit功能详解——超详细介绍

聊天室是当下泛娱乐社交应用中最经典的玩法&#xff0c;通过调用环信的 IM SDK 接口&#xff0c;可以快速创建聊天室。如果想根据自己业务需求对聊天室应用的 UI界面、弹幕消息、礼物打赏系统等进行自定义设计&#xff0c;最高效的方式则是使用环信的 ChatroomUIKit 。 文档地址…

解决JVM进程被系统杀掉问题

背景 服务A在测试环境&#xff0c;隔几个小时接口就无法访问。登录机器查看&#xff0c;发现进程已经没了。大致猜想是进程使用的内存或CPU资源使用太多&#xff0c;导致被系统kill。 问题定位 使用dmesg命令查看进程被kill的详情。 > dmesg --time-format iso2024-03-0…

29网课交单平台源码最新修复全开源版本

去除论文编辑 去除强国接码 修复上级迁移 修复聚合登录 修复支付不回调 优化支付接口兼容码/易支付 优化MySQL表&#xff0c;提高网页加载速度 下载地址&#xff1a;https://pan.xunlei.com/s/VNstLrJaRtbvDyovPQ-CbISOA1?pwd622t#

C# 第三方曲线库及其特点

在 C# 中&#xff0c;有几个第三方库可以用于绘制曲线图&#xff0c;每个库都有自己的特点和优势。以下是一些常见的 C# 第三方曲线库及其特点&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.LiveC…

跟着GPT学设计模式之桥接模式

说明 桥接模式&#xff0c;也叫作桥梁模式&#xff0c;英文是 Bridge Design Pattern。在 GoF 的《设计模式》一书中&#xff0c;桥接模式是这么定义的&#xff1a;“Decouple an abstraction from its implementation so that the two can vary independently。”翻译成中文就…

观测云在 .NET 业务中分析性能问题的最佳实践

背景 某药业集团是一家以创新技术驱动的线下医疗数据 SaaS 平台建设和运营公司&#xff0c;其主营的某智慧医疗平台产品&#xff0c;围绕线下医疗场景痛点提供一体化服务解决方案。近期集团对其生物检材在线递检系统进行功能升级开发及 IaaS 平台迁移。在针对新系统和新基础设…

WPF RichTextBox 使用Paragraph显示富文本(角标小数字)

最近项目中需要展示和编辑角标小数字&#xff0c;研究下了&#xff0c;可以RichTextBox 的Paragraph实现。 定义组件RichTextBox&#xff0c;添加右上角右下角小数字&#xff1a; <RichTextBox Margin"10" Height"40" Name"richTextBox">…