【算法题】算法之动态规划系列(基础篇)

news2025/1/6 18:34:30

算法之动态规划系列(基础篇)

  • 一、前置基础
  • 二、题目-- 爬楼梯
    • 2.1、思路
    • 2.2 代码实现
  • 三、题目--杨辉三角
    • 3.1、思路
    • 3.2、代码实现
  • 四、题目--买卖股票的最佳时机
    • 4.1、思路
    • 4.2、代码实现
    • 4.3、优化
  • 五、比特位计数
    • 5.1、思路
    • 5.2、代码实现(最高有效位法)
    • 5.3、代码实现(最低有效位法):
  • 总结

一、前置基础

动态规划,需要清楚如下:

  1. dp数组的含义以及dp下标的含义。
  2. 递推公式。
  3. dp数组如何初始化。
  4. 遍历顺序,如果是两个for循环,清晰先循环哪一个。
  5. 如果算法执行有问题,打印dp数组分析。

以下题目来源:力扣(LeetCode)

二、题目-- 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 1:

输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1)1 阶 + 1 阶
2)2 阶

示例 2:

输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1) 1 阶 + 1 阶 + 1 阶
2) 1 阶 + 2 阶
3) 2 阶 + 1 阶

2.1、思路

像这种求多少种可能性的题目一般都有递推性质,即f(n)和f(n-1)…f(1)之间是有联系的: f(n)=f(n-1)+f(n-2)。可转化为 求斐波那契数列第n项的值 ,唯一的不同在于起始数字不同。

用动态规划:

  1. 建立一维数组dp[2],下标分别对应的前一个的值。
  2. 递推公式:f(n)=f(n-1)+f(n-2);其中f(0)=1,f(1)=1。
  3. dp初始化:dp[0]=1,dp[1]=1。
  4. 只需要一个循环体。

2.2 代码实现

class Solution {
public:
    int climbStairs(int n) {
        if(n<2)
            return 1;
        int dp[2];
        dp[0]=1;
        dp[1]=1;
        int tmp;
        for(int i=2;i<=n;i++)
        {
            tmp=dp[0];
            dp[0]=dp[1];
            dp[1]=dp[0]+tmp;
        }
        return dp[1];
    }
};

三、题目–杨辉三角

给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。在「杨辉三角」中,每个数是它左上方和右上方的数的和。
杨辉三角

示例 1:

输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]

示例 2:

输入: numRows = 1
输出: [[1]]

3.1、思路

杨辉三角具有以下性质:

  1. 每行数字左右对称,由 1 开始逐渐变大再变小,并最终回到 1。

  2. 第 n 行(从 0 开始编号)的数字有 n+1 项,前 n 行共有 n ( n + 1 ) 2 \frac{n(n+1)}{2} 2n(n+1) 个数。

  3. 第 n 行的第 m 个数(从 0 开始编号)可以被表示为组合数 C(n,m) ,记作 C n m \mathcal{C}_n^m Cnm ( n m ) \binom{n}{m} (mn) ,即从 n 个不同元素中取 m 个元素的组合数。可以用公式来表示它: C n m = n ! m ! × ( n − m ) ! \mathcal{C}_n^m=\dfrac{n!}{m!\times (n-m)!} Cnm=m!×(nm)!n!

  4. 每个数字等于上一行的左右两个数字之和,可用此性质写出整个杨辉三角。即第 n 行的第 i 个数等于第 n-1行的第 i-1个数和第 i 个数之和。这也是组合数的性质之一,即 C n i = C n − 1 i + C n − 1 i − 1 \mathcal{C}_n^i=\mathcal{C}_{n-1}^i+\mathcal{C}_{n-1}^{i-1} Cni=Cn1i+Cn1i1

  5. ( a + b ) n (a+b)^n (a+b)n 的展开式(二项式展开)中的各项系数依次对应杨辉三角的第 n 行中的每一项。

根据性质,可以进行动态规划设计:

  1. 建立二维数组,用来存储杨辉三角的每一行数字,下标是杨辉三角的具体数字。
  2. 递推公式可以解释为:dp[i][j]=dp[i-1][j-1]+dp[i-1][j];即前一项的数组前一项的相加。
  3. 数组初始化,杨辉三角的性质是当前行的第一项和最后一项都是1,因此dp[i][0]=dp[i][max]=1。
  4. 需要两个循环体,先遍历行,再遍历列;在遍历列时,相当于一维数组的动态规划。

3.2、代码实现

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> res;//动态规划的二维数组
        
        for(int i=0;i<numRows;i++)
        {
            vector<int> tmp(i+1);
            // 初始化
            tmp[0]=1;
            tmp[i]=1;
            for(int j=1;j<i;j++)
            {
                tmp[j]=res[i-1][j-1]+res[i-1][j];
            }
            res.emplace_back(tmp);
        }
        return res;
    }
};

四、题目–买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

4.1、思路

题目只问最大利润,没有问这几天具体哪一天买、哪一天卖,因此可以考虑使用 动态规划 的方法来解决。

买卖股票有约束,根据题目意思,有以下两个约束条件:

条件 1:你不能在买入股票前卖出股票;
条件 2:最多只允许完成一笔交易。

因此 当天是否持股 是一个很重要的因素,而当前是否持股和昨天是否持股有关系,为此我们需要把 是否持股 设计到状态数组中。

状态定义:dp[i][j]是下标为 i 这一天结束的时候,手上持股状态为 j 时,我们持有的现金数。也就是说dp[i][j] 表示天数 [0, i] 区间里,下标 i 这一天状态为 j 的时候能够获得的最大利润。其中:

j = 0,表示当前不持股;
j = 1,表示当前持股。

推导状态转移方程:

dp[i][0]:规定了今天不持股,有以下两种情况:

昨天不持股,今天什么都不做;
昨天持股,今天卖出股票(现金数增加),

dp[i][1]:规定了今天持股,有以下两种情况:

昨天持股,今天什么都不做(现金数与昨天一样);
昨天不持股,今天买入股票(注意:只允许交易一次,因此手上的现金数就是当天的股价的相反数)。

4.2、代码实现

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n=prices.size();
        if(n<2)
            return 0;
        vector<vector<int>> dp(n,vector<int>(2));
        // dp[i][0] 下标为 i 这天结束的时候,不持股,手上拥有的现金数
        // dp[i][1] 下标为 i 这天结束的时候,持股,手上拥有的现金数
        dp[0][0]=0;
        dp[0][1]=-prices[0];
        for(int i=1;i<n;i++)
        {
            dp[i][0]=max(dp[i-1][0],prices[i]+dp[i-1][1]);
            dp[i][1]=max(dp[i-1][1],-prices[i]);
        }
        return dp[n-1][0];
        

    }
};

4.3、优化

空间优化只看状态转移方程。

状态转移方程里下标为 i 的行只参考下标为 i - 1 的行(即只参考上一行),并且:

下标为 i 的行并且状态为 0 的行参考了上一行状态为 0 和 1 的行;
下标为 i 的行并且状态为 1 的行只参考了上一行状态为 1 的行。

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

五、比特位计数

给一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。

示例 1:

输入:n = 2
输出:[0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10

示例 2:

输入:n = 5
输出:[0,1,1,2,1,2]
解释:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101

5.1、思路

第一种:最高有效位法。在区间[0,i]中找到一个数y,y ≤ i,且y的最高位为1而其他位都为0(y&(y-1)==0),那么就可以计算bit[i]=bit[i-y]+1。

第二种:最低有效位。第一种方法需要实时维护最高有效位,当遍历到的数是2的整数次幂时,需要更新最高有效位。对于正整数 xx,将其二进制表示右移一位,等价于将其二进制表示的最低位去掉,得到的数是
。如果 bits [ ⌊ x 2 ⌋ ] \textit{bits}\big[\lfloor \frac{x}{2} \rfloor\big] bits[2x] 的值已知,则可以得到 bits [ x ] \textit{bits}[x] bits[x] 的值:bits[x]= bits [ ⌊ x 2 ⌋ ] \textit{bits}\big[\lfloor \frac{x}{2} \rfloor\big] bits[2x] +(x&1)。

第三种:最低设置位。推导公式:bits[x]=bits[x&(x−1)]+1。

5.2、代码实现(最高有效位法)

class Solution {
public:
    vector<int> countBits(int n) {
        vector<int> ans(n+1);
        ans[0]=0;
        int hightbit=0;//维护最高有效位
        for(int i=1;i<=n;i++)
        {
            if((i&(i-1))==0)
                hightbit=i;
            ans[i]=ans[i-hightbit]+1;
        }
        return ans;
    }
};

5.3、代码实现(最低有效位法):

class Solution {
public:
    vector<int> countBits(int n) {
        vector<int> ans(n+1);
        ans[0]=0;
        for(int i=1;i<=n;i++)
        {
            ans[i]=ans[i/2]+(i&1);
        }
        return ans;
    }
};

总结

动态规划常常用于求解多阶段决策问题。

一定要做好总结,特别是当没有解出题来,没有思路的时候,一定要通过结束阶段的总结来反思犯了什么错误。解出来了也一定要总结题目的特点,题目中哪些要素是解出该题的关键。不做总结的话,花掉的时间所得到的收获通常只有 50% 左右。

在题目完成后,要特别注意总结此题最后是归纳到哪种类型中,它在这种类型中的独特之处是什么。
在这里插入图片描述

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

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

相关文章

大数据从0到1的完美落地之sqoop命令执行

Sqoop命令执行 常见命令执行参数 通过Sqoop加不同参数可以执行导入导出,通过sqoop help 可以查看常见的命令行 #常见Sqoop参数 [rootqianfeng01 sqoop-1.4.7] sqoop helpcodegen Generate code to interact with database recordscreate-hive-table Import a ta…

DC-DC降压恒流芯片 12-110V 9V/1A AP2400 LED汽车摩托车灯驱动IC

1,方案来源&#xff1a;深圳市世微半导体有限公司 2&#xff0c;产品BOM表&#xff1a;输入 12-100V 输出9V 0.9A 3&#xff0c;产品线路图&#xff1a;输入 12-100V 输出9V 0.9A 4&#xff0c;产品介绍 AP2400 是一款 PWM 工作模式, 率、外围简单、外驱功率管&#xff0…

Axure教程—复选框全选与反选

很多业务系统之中都会有以列表的形式展现数据的情况&#xff0c;那么问题来了&#xff0c;对列表选择时会出现多选、全选、反选的问题&#xff0c;今天就来介绍如何使用axure原型工具实现复选框的多选、全选和反选。 效果预览 预览地址&#xff1a;https://ddprtm.axshare.com…

FFmpeg流媒体处理的收流与推流

1. 简介 流媒体是使用了流式传输的多媒体应用技术。如下是维基百科关于流媒体概念的定义&#xff1a; 流媒体 (streaming media) 是指将一连串的媒体数据压缩后&#xff0c;经过网络分段发送数据&#xff0c;在网络上即时传输影音以供观赏的一种技术与过程&#xff0c;此技术使…

20亿元项目“落子”杭州,这家企业加速全栈智驾平台规模化交付

6月20日&#xff0c;在“幸会杭州”投资促进大会上&#xff0c;中国领先的智能驾驶解决方案服务商和产品供应商福瑞泰克与杭州市政府就在杭州市高新技术开发区&#xff08;滨江&#xff09;建设全球研发管理总部达成签约。福瑞泰克董事长兼首席执行官张林博士受邀出席大会&…

SSM摄影服务线上选购预约系统-计算机毕设 附源码83784

SSM摄影服务线上选购预约系统 摘 要 随着互联网趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己推广出去&#xff0c;最好方式就是建立自己的互联网系统&#xff0c;并对其进行维护和管理。在现实运用中&#xff0c;应用软件的工作规则和开发步骤&#xff0c;采用SSM技…

网络安全|渗透测试入门学习,从零基础入门到精通—动态分析技术OllyDbg案例详情

目录 1、准备工作 2、加载目标文件进行调试 3、单步跟踪 4、设置断点 5、调试分析 1、准备工作 分析一个Windows程序要比分析一个DOS程序容易得多因为在Windows中只要API函数被使用了&#xff0c;再想对要寻找蛛丝马迹的人隐藏一些信息就比较困难了。因此&#xff0c;在分析…

软考高级系统架构设计师(七) 需求工程相关

目录 需求工程概述 需求工程-需求获取 需求开发-需求分析 需求分析-OOA OOA-UML-41视图 需求定义 需求验证 需求跟踪 需求变更管理 软件系统建模 结构化设计 面向对象设计 软件界面设计 需求工程概述 需求工程-需求获取 需求获取方法&#xff1a; 需求开发-需求分…

在 BI 工具 Tableau 中使用 databend

Tableau是一款流行的数据可视化和业务智能工具。它提供了直观、交互式的方式来探索、分析和呈现数据&#xff0c;帮助用户更好地理解数据的意义和洞察。 本教程帮助用户在 Tableau 中连接 databend 进行 BI 数据分析。 Databend jdbc Tableau 支持所有实现了 JDBC 协议的数据…

如何在 Spring Boot 中使用 WebFlux

如何在 Spring Boot 中使用 WebFlux 随着互联网应用的复杂度不断增加&#xff0c;传统的请求响应模型已经无法满足日益增长的需求。传统的 Web 应用使用 Servlet 容器&#xff0c;采用同步阻塞的方式来处理请求&#xff0c;请求需要等待相应的处理逻辑完成后才能返回结果。这种…

【C/C++实战项目】万年历

目录 项目分析 项目效果 头文件及全局变量 获取天数 打印月份、年份日历 main函数 项目分析 实现查询某一个年份、月份&#xff0c;以日历的格式打印为了观赏性利用符号对打印的每一个日期进行分格特殊节日日期能够在日历中标注出来万年历的起始日期是公元1年&#xff0…

[Orin Nx] 如何跑满GPU和CPU,观察温度和散热性能?

1、环境说明 硬件: Nvidia Orin NX 16GB 软件&#xff1a;Jetson Linux R35.3.1 2、工具安装部署 GPU的压力测试主要使用工具&#xff1a; https://github.com/anseeto/jetson-gpu-burn CPU的压力测试主要使用工具 stress 注意安装 jetson-gpu-burn需要在 /etc/apt/sourc…

SpringBoot 客户信息管理系统-计算机毕设 附源码80944

SpringBoot客户信息管理系统 摘 要 随着计算机技术的发展&#xff0c;特别是计算机网络技术与数据库技术的发展&#xff0c;使用人们的生活与工作方式发生了很大的改观。本课题研究的客户信息管理系统&#xff0c;主要功能模块包括统计图&#xff0c;系统用户&#xff08;管理…

JS高级用法:像大神一样玩转JavaScript

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 前言 众所周知&#xff0c;JavaScript是一种非常流行的编程语言&#xff0c;它已经成为了…

回收站如何添加到桌面?没有回收站的数据怎么恢复

最近收到很多这样的咨询&#xff1a; 咨询1&#xff1a;在网吧上网&#xff0c;不小心把照片删了&#xff0c;可恨的是这里没有回收站&#xff0c;请问怎么找回照片&#xff01;&#xff01;&#xff01;急&#xff0c;谢谢了。 咨询2&#xff1a;在网吧上网&#xff0c;一删除…

emmylua+Rider 断点调试

1、下载安装并激活好rider&#xff08;本次版本基于&#xff1a;JetBrains Rider 2023.1.3&#xff09; 2、从插件商店中安装emmylua 3、打开项目 在unity中设置好&#xff0c;使用rider打开项目即可 4、Unity中打开相应的场景 &#xff08;此场景基于Xlua官方Demo展示&…

初中生台灯如何选择?眼科专家推荐的学生台灯分享

保护视力一直是家长们经常提醒孩子们的一句话&#xff0c;但据统计青少年学生的近视率还是非常高&#xff0c;很多家长认为这是不良的用眼习惯导致的&#xff0c;却没想过可能是其他原因&#xff0c;因为孩子除了自身保持良好的用眼习惯外&#xff0c;在学习时用眼的光线也是十…

有哪些智能聊天ai机器人?这几种ai工具都能进行聊天

在当前ai技术日益成熟的时代&#xff0c;越来越多的ai聊天机器人开始走进我们的生活中&#xff0c;为我们带来更多乐趣和便利。这几款ai聊天机器人不仅可以陪伴我们度过孤独的夜晚&#xff0c;还可以为我们提供各种对话服务。它们能理解我们的话语&#xff0c;与我们进行自然对…

【Linux 系统编程】Linux背景介绍 及 基本指令详解(一)

文章目录 1. Linux背景1.1 发展史1.2 开源精神1.3 官网1.4 企业应用现状1.5 发行版本1.6 os概念&#xff0c;定位 2. Linux下基本指令2.1 几个常用基本命令演示2.2 某些概念的解释2.3 ls 指令2.4 pwd 指令2.5 cd 指令Linux的目录结构绝对路径与相对路径cd 常用快捷命令 2.6 tou…

【unity每日一记】transform类精华之珠

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…