【数据结构】动态规划-基础篇

针对动态规划问题,我总结了以下5步:

确定dp数组以及下标的含义;

递推公式;

dp数组如何初始化;

遍历顺序;

打印dp数组(用来debug);

以上5步适用于任何动态规划问题,下面针对题目,我们来具体实践:

说明:本题代码均为力扣AC代码。

题目一:斐波那契数

class Solution {
public:int fib(int n) {//1.dp[i]表示第i个斐波那契数的值//2.递推公式 dp[i] = dp[i-1] + dp[i-2]//3.dp[0] = 0 dp[1] = 1//4.遍历顺序:本题从前到后遍历即可if(n == 0 || n == 1)return n;vector<int>dp(n+1);dp[0] = 0;dp[1] = 1;for(int i = 2;i <= n;++i){dp[i] = dp[i-1] + dp[i-2];}return dp[n];//返回第n个斐波那契数的值}
};

当然,本题非常简单,不使用动态规划也是OK的。

class Solution {
public:int fib(int n) {//迭代if(n == 0 || n== 1)return n;vector<int>f(n+1);f[0] = 0;f[1] = 1;int cur = 0;for(int i = 2;i <= n;++i){cur = f[0] + f[1];f[0] = f[1];f[1] = cur;}return cur;}
};

 题目二:爬楼梯

分析一波:为啥递推公式是dp[i] = dp[i-1]+dp[i-2]?dp[i]为到达第i阶有dp[i]种方法,.dp[i-1]为到达第i-1阶有dp[i-1]种方法,.dp[i-2]为到达第i-2阶有dp[i-2]种方法,要想到达第i阶,只需从第i-1阶爬一阶或者从第i-2阶爬二阶即可,所以dp[i] = dp[i-1]+dp[i-2]。

class Solution {
public:int climbStairs(int n) {//1.dp[i]为到达第i阶有dp[i]种方法//2.递推公式:dp[i] = dp[i-1]+dp[i-2]//3.dp[1] = 1,dp[2] = 2//遍历顺序:因为dp[i]依赖于dp[i-1]、dp[i-2],所以应该从前到后遍历vector<int> dp(n + 1);if(n == 1 || n == 2)return n;dp[1] = 1;dp[2] = 2;for(int i=3;i<=n;++i){dp[i] = dp[i-1]+dp[i-2];}return dp[n];//爬到第n阶一共有多少种方法}
};

题目三: 使用最小花费爬楼梯

分析一波:本题和上一道爬楼梯很相似,不过是加上了个花费,这里dp[i]为到达i阶楼梯最小的花费,要想到达第i阶,只需从第i-1阶爬一阶或者从第i-2阶爬二阶即可,从第i-1阶爬到第i阶需要花费dp[i-1]+cost[i-1],从第i-2阶爬到第i阶需要花费dp[i-2]+cost[i-2],本题要求最小花费,所以状态转移方程为dp[i] = min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]).

对于初始化,本题说了可以选择从下标为0或1的元素作为初始阶梯,还要注意一点是不跳不花费体力,所以dp[0] = 0,dp[1] = 0.

对于遍历顺序,由于dp[i]是依靠dp[i-1]和dp[i-2]推导的,所以遍历顺序是从前到后。

分析完以后,就能很丝滑的做出来啦!

class Solution {
public:int minCostClimbingStairs(vector<int>& cost) {int n = cost.size();vector<int>dp(n+1);dp[0] = 0;dp[1] = 0;for(int i=2;i<=n;++i){dp[i] = min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);}return dp[n];}
};

题目四:不同路径

分析一波:本题是二维矩阵,所以dp数组应该定义成二维的,dp[i][j]代表从(0,0)位置走到(i,j)位置有多少种不同的路径,可以看到,如果想到达(i,j)的位置,只能从(i,j-1)的位置走一步或者从(i-1,j)的位置向下走一步,所以dp[i][j] = dp[i][j-1]+dp[i-1][j].

对于初始化,要想到达(i,j)的位置,要么从上面过来,要么从左边过来,所以我们要把最左边和最上边都初始化,初始化成多少呢?本题要求只能向右或者向下走,所以最上面行从最左侧走到最右侧只有一种走法,最左侧的列中从最上到最下也只有一种走法,所以初始化如下图。

class Solution {
public:int uniquePaths(int m, int n) {//创建m行n列的二维数组vector<vector<int>>dp(m,vector<int>(n));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];}
};

题目五:不同路径II

本题和上一题类似,只是本题多了一个障碍物,对于状态转移方程,如果(i,j)位置有障碍的话,那么我们无法继续推导,所以我们需要添加一个条件就是当(i,j)位置不是障碍物时,我们进行推导,否则不去推导。对于初始化,和上一题不同的是,第一列如果有障碍物的话,障碍物后面的位置都无法到达,第一行也是如此,所以我们在初始化时应该加上一个条件,就是当前位置没有障碍物,我们才给dp[i][0]、dp[0][j]初始化成1.

class Solution {
public:int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {int m = obstacleGrid.size();int n = obstacleGrid[0].size();if(obstacleGrid[0][0] == 1 || obstacleGrid[m-1][n-1] == 1)return 0;//创建m行n列的二维数组vector<vector<int>>dp(m,vector<int>(n));//dp数组初始化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])dp[i][j] = dp[i-1][j] + dp[i][j-1];}}return dp[m-1][n-1];}
};

题目六:整数拆分

1.确定dp数组含义:dp[i]表示将i进行拆分后得到最大的乘积

2.确定递推公式:将i拆分成两个数最大积为j*(i-j),拆分成三个及以上为j*dp[i-j],这里有个问题,为什么j不拆?实际上,在我们拆分 dp[i-j] 过程中已经包含了拆分 j 的情况,所以这里只考虑如何对 i-j 进行拆分即可,所以递推公式为dp[i] = max(dp[i],max(j*(i-j),j*dp[i-j]))

3.dp数组如何初始化?根据dp数组含义,dp[0] = 0,dp[1] = 0,dp[2] = 1

4.遍历顺序:根据递推公式可以看出,dp[i]的状态依靠dp[i-j]的状态,所以是从前到后遍历。

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

对于本题,可以做一个小小的优化。对于拆分i使之乘积最大,一定是拆分成m个近似相同的子数才能得到最大乘积。只不过m我们不知道是几,但是可以确定的是m一定大于等于2,所以在判断条件中,只需要 j <= i/2 即可。举个例子,拆分10的话,可以拆分成5*5,也可以拆分成3*3*4,如果拆分成6*4,后续无论对4如何拆分都不可能得到最大的,因为我们要把i拆分成近似相同的子数才能得到最大值。

题目七:

1.明确dp数组及下标含义:1到i为节点的二叉搜索树的个数为dp[i]

2.递推公式:根据图中分析,dp[3]就是以元素1为头结点BST的数量+以元素2为头结点BST的数量+以元素3为头结点BST的数量,我们要计算dp[i],只需要让 j 从遍历 1 到 i,计算 j 为头结点对应BST的个数并将他们相加即可。注意,j为头结点时,其左子树数目为 j-1 个,右子树数目为 i-j 个状态转移方程:dp[i] += dp[j-1]*dp[i-j].

3.如何初始化?dp[0] = 1,因为空BST也是BST

4.遍历顺序:从前到后

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

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

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

相关文章

字符串函数和内存函数

字符串函数 1、strlcpy 【字符串拷贝】 &#xff08;将原字符串中的字符拷贝到目标字符数组中&#xff0c;包括终止符号\0&#xff0c;并在这里停止&#xff1b;为了避免越界&#xff0c;目标字符串数组应该足够大去接收&#xff09;&#x1f446; &#xff08;返回值是 dest…

Electron-vue 框架升级 Babel7 并支持electron-preload webapck 4 打包过程记录

前言 我这边一直用的electron-vue框架是基于electron 21版本的&#xff0c;electron 29版本追加了很多新功能&#xff0c;但是这些新功能对开发者不友好&#xff0c;对electron构建出来的软件&#xff0c;使用者更安全&#xff0c;所以&#xff0c;我暂时不想研究electron 29版…

ML 系列:第 39 节 - 估计方法:最大似然估计 (MLE)

目录 一、说明 二、什么是最大似然估计 (MLE)&#xff1f; 2.1 理解公式 2.2 MLE 的定义 2.3 我们何时使用 MLE&#xff1f; 三、结论 一、说明 在统计学领域&#xff0c;我们经常需要根据观察到的数据估计统计模型的参数。为此目的广泛使用的两种关键方法是最大似然估计 ( MLE…

文本生成类(机器翻译)系统评估

在机器翻译任务中常用评价指标&#xff1a;BLEU、ROGUE、METEOR、PPL。 这些指标的缺点&#xff1a;只能反应模型输出是否类似于测试文本。 BLUE&#xff08;Bilingual Evaluation Understudy&#xff09;&#xff1a;是用于评估模型生成的句子(candidate)和实际句子(referen…

vue.js学习(day 19)

自定义创建项目 ESlint 代码规范 代码规范错误 手动修正 自动修正 settings.json {"emmet.triggerExpansionOnTab": true,"editor.fontSize": 25,// 当保存的时候&#xff0c;eslint自动帮我们修复错误"editor.codeActionsOnSave": {&qu…

两个畸变矩阵相乘后还是一个2*2的矩阵,有四个畸变元素。1、畸变矩阵吸收了法拉第矩阵。2、畸变矩阵也给法拉第旋转角带来模糊(求解有多种可能)

角度一&#xff1b;恢复畸变的时候也把法拉第旋转恢复了 角度二&#xff1a;求解法拉第旋转角的时候 前面乘的复系数的不同也会带来法拉第旋转角和畸变的不同解 注意&#xff1a;无论多少个畸变矩阵相乘&#xff0c;结果都是2*2的矩阵&#xff0c;也就是畸变参数可以减少…

VR眼镜可视化编程:开启医疗信息系统新纪元

一、引言 随着科技的飞速发展&#xff0c;VR 可视化编程在医疗信息系统中的应用正逐渐成为医疗领域的新趋势。它不仅为医疗教育、手术培训、疼痛管理等方面带来了新的机遇&#xff0c;还在提升患者体验、推动医疗信息系统智能化等方面发挥着重要作用。 在当今医疗领域&#xf…

禾川Q1系列PLC控制X3E总线伺服

1、建立链接 2、配置EtherCAT总线 3、添加Cia402轴 4、添加总线设备 5、总线轴控FB建立 代码部分1

股市复盘笔记

复盘是股市投资中非常重要的一个环节&#xff0c;它指的是投资者在股市收盘后&#xff0c;对当天的市场走势、个股表现以及自己的交易行为进行回顾和总结&#xff0c;以便更好地指导未来的投资决策。以下是对复盘的详细解释&#xff1a; 一、复盘的目的 总结市场走势&#xff…

基于TensorFlow框架的线性回归实现

目录 ​编辑 线性回归简介 TensorFlow简介 线性回归模型的TensorFlow实现 1. 安装TensorFlow 2. 导入必要的库 3. 准备数据 4. 定义模型 5. 定义损失函数 6. 定义优化器 7. 训练模型 8. 评估模型 9. 模型参数的可视化 10. 模型预测的准确性评估 结论 在统计学和…

网页端五子棋对战(四)---玩家匹配实现上线下线处理

文章目录 1.游戏大厅用户匹配1.1请求和响应1.2设计匹配页面1.3获取玩家信息1.4玩家信息的样式设置1.5初始化我们的websocket1.6点击按钮和客户端交互1.7点击按钮和服务器端交互 2.服务器端实现匹配功能框架2.1方法重写2.2借用session 3.处理上线下线3.1什么是上线下线3.2实现用…

「Mac畅玩鸿蒙与硬件42」UI互动应用篇19 - 数字键盘应用

本篇将带你实现一个数字键盘应用&#xff0c;支持用户通过点击数字键输入数字并实时更新显示内容。我们将展示如何使用按钮组件和状态管理来实现一个简洁且实用的数字键盘。 关键词 UI互动应用数字键盘按钮组件状态管理用户交互 一、功能说明 数字键盘应用将实现以下功能&…

cgo内存泄漏排查

示例程序&#xff1a; package main/* #include <stdlib.h> #include <string.h> #include <stdio.h> char* cMalloc() {char *mem (char*)malloc(1024 * 1024 * 16);return mem; } void cMemset(char* mem) {memset(mem, -, 1024 * 1024 * 16); } int arr…

红日靶场vulnstack (五)

前言 好久没打靶机了&#xff0c;今天有空搞了个玩一下&#xff0c;红日5比前面的都简单。 靶机环境 win7&#xff1a;192.168.80.150(外)、192.168.138.136(内) winserver28&#xff08;DC&#xff09;&#xff1a;192.168.138.138 环境搭建就不说了&#xff0c;和之前写…

汽车IVI中控开发入门及进阶(三十七):基于HFP协议的蓝牙电话

概述: HFP全称Hands-free Profile,是一款让蓝牙设备控制电话的软件,多用于汽车上。此类设备最常见的例子是车载免提装置与蜂窝电话或可穿戴无线耳机一起使用。该配置文件定义了支持免提配置文件的两个设备如何在点对点的基础上相互交互。免提模式的实现通常使耳机或嵌入式免…

线程条件变量 生产者消费者模型 Linux环境 C语言实现

只能用来解决同步问题&#xff0c;且不能独立使用&#xff0c;必须配合互斥锁一起用 头文件&#xff1a;#include <pthread.h> 类型&#xff1a;pthread_cond_t PTHREAD_COND_INITIALIZER 初始化 初始化&#xff1a;int pthread_cond_init(pthread_cond_t * cond, NULL);…

AI技术在电商行业中的应用与发展

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

高通---Camera调试流程及常见问题分析

文章目录 一、概述二、Camera配置的整体流程三、Camera的代码架构图四、Camera数据流的传递五、camera debug FAQ 一、概述 在调试camera过程中&#xff0c;经常会遇到各种状况&#xff0c;本篇文章对camera调试的流程进行梳理。对常见问题的提供一些解题思路。 二、Camera配…

高危端口汇总(Summary of High-Risk Ports)

高危端口汇总 能关闭就关闭 &#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解…

贪心算法实例-问题分析(C++)

贪心算法实例-问题分析 饼干分配问题 有一群孩子和一堆饼干&#xff0c;每个小孩都有一个饥饿度&#xff0c;每个饼干都有一个能量值&#xff0c;当饼干的能量值大于等于小孩的饥饿度时&#xff0c;小孩可以吃饱&#xff0c;求解最多有多少个孩子可以吃饱?(注:每个小孩只能吃…