算法学习笔记Day9——动态规划初探

2024-04-25 03:04

本文主要是介绍算法学习笔记Day9——动态规划初探,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、介绍

本文解决几个问题:动态规划是什么?解决动态规划问题有什么技巧?如何学习动态规划?

1. 动态规划问题的一般形式就是求最值。动态规划其实是运筹学的一种最优化方法,只不过在计算机问题上应用比较多,比如说让你求最长递增子序列呀,最小编辑距离呀等等。

2. 动态规划的核心思想就是穷举求最值,但只有列出正确的「状态转移方程」,才能正确地穷举。你需要判断算法问题是否具备「最优子结构」,是否能够通过子问题的最值得到原问题的最值。另外,动态规划问题存在「重叠子问题」,如果暴力穷举的话效率会很低,所以需要你使用「备忘录」或者「DP table」来优化穷举过程,避免不必要的计算。

以上提到的重叠子问题、最优子结构、状态转移方程就是动态规划三要素。

3. 思维框架:明确 base case -> 明确「状态」-> 明确「选择」 -> 定义 dp 数组/函数的含义

递归是自顶向下,动态规划是自底向上

4. 带备忘录的递归和动态规划实际上是等价的,动态规划是从底层开始,一步一步完成对数组的完善,所以不需要备忘录,或者说dp数组本身就是备忘录,递归会遇到很多重复的子问题,所以需要备忘录来简化。

二、例题

例题1:斐波那契数

分析

写出状态转移方程,写出基底,就可以开始自底向上构造了。

代码

思路1:自底向上解法

class Solution {
public:int fib(int n) {if(n == 0 || n == 1){return n;}int fib_1 = 1, fib_2 = 0;int fib_i;for(int i = 2; i<= n; i++){fib_i = fib_1 + fib_2;fib_2 = fib_1;fib_1 = fib_i;}return fib_i;}
};

思路2:自顶向下解法

class Solution {
public:vector<int> diary;int recursion(int n){//基地if(n == 0 || n == 1){return n;}//查日记if(diary[n] != -1){return diary[n];}//日记没查到,更新日记,用递归更新它diary[n] = recursion(n-1) + recursion(n-2);//再查找日记本return diary[n];}int fib(int n) {diary.resize(n+1, -1);return recursion(n);}
};

例题2:零钱兑换

分析

写出状态方程就可以了

代码

思路1:带备忘录的递归

class Solution {
public:vector<int> diary;int dp(vector<int>& coins, int amount){if(amount < 0){return -1;}if(amount == 0){return 0;}if(diary[amount] != 0){return diary[amount];}int ans = INT_MAX;for(int coin : coins){int subsolution = dp(coins, amount - coin);if(subsolution != -1){ans = min(subsolution+1, ans);}}diary[amount] = ans==INT_MAX ? -1:ans;return diary[amount];}int coinChange(vector<int>& coins, int amount) {diary.resize(amount+1, 0);return dp(coins, amount);}
};

不知道为什么把diary初始化为-1就会超时,推测是-1表示不可能的情况,有很多正数diary也是-1,就容易进入循环,但是0只有0这个情况。

思路2:dp迭代

class Solution {
public:int coinChange(vector<int>& coins, int amount) {vector<int> dp(amount + 1, INT_MAX-1);//base situationdp[0] = 0;for(int i  =0; i<=amount; i++){for(int coin : coins){if(i - coin < 0){continue;}dp[i] = min(dp[i], dp[i-coin] + 1);}}return dp[amount]==INT_MAX-1?-1:dp[amount];}
};

例题3:最长递增子序列

分析

首先要明确dp数组代表什么,这里是以 位置i数字 结尾的最长字序列长度,对于每个位置,比较前面的位置,只要它大于某个元素,就可以和那个元素的最长子序列组成新的最长子序列。

代码

class Solution {
public:int lengthOfLIS(vector<int>& nums) {vector<int> dp(nums.size(), 1);for(int i  = 0; i< nums.size(); i++){for(int j = 0; j<i; j++){if(nums[i] > nums[j])dp[i] = max(dp[i], dp[j] + 1);}}return *max_element(dp.begin(), dp.end());}
};

例题4:俄罗斯套娃信封问题 

代码

class Solution {
public:int maxEnvelopes(vector<vector<int>>& envelopes) {int n = envelopes.size();sort(envelopes.begin(), envelopes.end(), [](vector<int>& a, vector<int>& b)->bool{return a[0] == b[0]? a[1] > b[1] : a[0] < b[0];});vector<int> dp(n, 1);for(int i = 0; i<n; i++){for(int j = 0; j< i; j++){if(envelopes[i][1] > envelopes[j][1]){dp[i] = max(dp[i], dp[j] + 1);}}}return *max_element(dp.begin(), dp.end());}
};

 

三、总结

i)斐波那契数列的问题,解释了如何通过「备忘录」或者「dp table」的方法来优化递归树,并且明确了这两种方法本质上是一样的,只是自顶向下和自底向上的不同而已。

ii)凑零钱的问题,展示了如何流程化确定「状态转移方程」,只要通过状态转移方程写出暴力递归解,剩下的也就是优化递归树,消除重叠子问题而已。

iii)二维数组也可以排序,要传入一个lamda表达式来说明排序的方式,第四题套娃问题,同样长的信封必须按宽的逆序排列,因为同样大小是不可以嵌套的,如果顺序排列,求最长递增子序列的时候就会多一个。

这篇关于算法学习笔记Day9——动态规划初探的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/933546

相关文章

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

Python学习笔记之getattr和hasattr用法示例详解

《Python学习笔记之getattr和hasattr用法示例详解》在Python中,hasattr()、getattr()和setattr()是一组内置函数,用于对对象的属性进行操作和查询,这篇文章... 目录1.getattr用法详解1.1 基本作用1.2 示例1.3 原理2.hasattr用法详解2.

浅谈MySQL的容量规划

《浅谈MySQL的容量规划》进行MySQL的容量规划是确保数据库能够在当前和未来的负载下顺利运行的重要步骤,容量规划包括评估当前资源使用情况、预测未来增长、调整配置和硬件资源等,感兴趣的可以了解一下... 目录一、评估当前资源使用情况1.1 磁盘空间使用1.2 内存使用1.3 CPU使用1.4 网络带宽二、

go动态限制并发数量的实现示例

《go动态限制并发数量的实现示例》本文主要介绍了Go并发控制方法,通过带缓冲通道和第三方库实现并发数量限制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录带有缓冲大小的通道使用第三方库其他控制并发的方法因为go从语言层面支持并发,所以面试百分百会问到

一文详解SpringBoot中控制器的动态注册与卸载

《一文详解SpringBoot中控制器的动态注册与卸载》在项目开发中,通过动态注册和卸载控制器功能,可以根据业务场景和项目需要实现功能的动态增加、删除,提高系统的灵活性和可扩展性,下面我们就来看看Sp... 目录项目结构1. 创建 Spring Boot 启动类2. 创建一个测试控制器3. 创建动态控制器注

Java中的雪花算法Snowflake解析与实践技巧

《Java中的雪花算法Snowflake解析与实践技巧》本文解析了雪花算法的原理、Java实现及生产实践,涵盖ID结构、位运算技巧、时钟回拨处理、WorkerId分配等关键点,并探讨了百度UidGen... 目录一、雪花算法核心原理1.1 算法起源1.2 ID结构详解1.3 核心特性二、Java实现解析2.

springboot如何通过http动态操作xxl-job任务

《springboot如何通过http动态操作xxl-job任务》:本文主要介绍springboot如何通过http动态操作xxl-job任务的问题,具有很好的参考价值,希望对大家有所帮助,如有错... 目录springboot通过http动态操作xxl-job任务一、maven依赖二、配置文件三、xxl-

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

Java调用C#动态库的三种方法详解

《Java调用C#动态库的三种方法详解》在这个多语言编程的时代,Java和C#就像两位才华横溢的舞者,各自在不同的舞台上展现着独特的魅力,然而,当它们携手合作时,又会碰撞出怎样绚丽的火花呢?今天,我们... 目录方法1:C++/CLI搭建桥梁——Java ↔ C# 的“翻译官”步骤1:创建C#类库(.NET

MyBatis编写嵌套子查询的动态SQL实践详解

《MyBatis编写嵌套子查询的动态SQL实践详解》在Java生态中,MyBatis作为一款优秀的ORM框架,广泛应用于数据库操作,本文将深入探讨如何在MyBatis中编写嵌套子查询的动态SQL,并结... 目录一、Myhttp://www.chinasem.cnBATis动态SQL的核心优势1. 灵活性与可