算法学习笔记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 situation
        dp[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表达式来说明排序的方式,第四题套娃问题,同样长的信封必须按宽的逆序排列,因为同样大小是不可以嵌套的,如果顺序排列,求最长递增子序列的时候就会多一个。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序小游戏开发,微信开发者工具提示该目录下的项目(wxapp2)已在工具中创建,怎么办

微信小程序小游戏开发&#xff0c;微信开发者工具提示该目录下的项目&#xff08;wxapp2&#xff09;已在工具中创建&#xff0c;怎么办 情况描述&#xff0c; 导入一个项目的时候&#xff0c;导入成了小游戏项目了 想换成小游戏项目&#xff0c;变不了了&#xff0c;提示 “…

未来已来:解锁AGI的无限潜能与挑战

未来已来&#xff1a;解锁AGI的无限潜能与挑战 引言 假设你有一天醒来&#xff0c;发现你的智能手机不仅提醒你今天的日程&#xff0c;还把你昨晚做的那个奇怪的梦解释了一番&#xff0c;并建议你可能需要减少咖啡摄入量——这不是科幻电影的情节&#xff0c;而是人工通用智能…

解决Milvus官网提供的单机版docker容器无法启动,以及其它容器进程与Milvus容器通信实现方案【Milvus】【pymilvus】【Docker】

文章目录 问题预备知识方案获取pymilvus获取milvus 实例多容器通信 问题 我的需求是做混合检索单机版可以满足&#xff0c;要走Docker容器部署&#xff0c;还需要和另一个容器中的程序做通信。官方文档提供的Milvus安装启动Milvus方案&#xff0c;见文档&#xff1a;传送门 我…

wlan二层直连组网实验(ensp)

目录 1. VLAN 端口类型及参数设计2. IP 地址规划3. WLAN数据规划(1) DHCP服务器配置(2) AC 源接口地址、认证方式配置(3) AP 组的创建(4) 创建域管理模板、国家码认证(5) 创建安全模板(6) 创建SSID模板(7) 创建VAP模板(8) AP组绑定模板(9) 查看&#xff1a; 1. VLAN 端口类型及…

以太网LAN双向透明传输CH9120透传芯片实现以太网转232串口转485转TTL串口

网络串口透传芯片 CH9120 1、概述 CH9120 是一款网络串口透传芯片。CH9120 内部集成 TCP/IP 协议栈&#xff0c;可实现网络数据包和串口数据的双向透明传输&#xff0c;具有 TCP CLIENT、TCP SERVER、UDP CLIENT 、UDP SERVER 4 种工作模式&#xff0c;串口波特率最高可支持到…

03 Docker入门Dockerfile详解及镜像创建

1.1 使用 Dockerfile 构建镜像 新建一个 Dockerfile 文件vi Dockerfile 将下面的内容复制粘贴进去:## Base Images ## 从天池基础镜像构建(from的base img 根据自己的需要更换,建议使用天池open list镜像链接:https://tianchi.aliyun.com/forum/postDetail?postId=67720) F…

【Unity动画系统】动画状态基本属性与相关API、IK简单概述

动画状态基本属性与相关API Tag&#xff1a;判断是否当前播放着相对应Tag的动画&#xff0c;如果是&#xff0c;那么玩家的输入就是无效的。 using UnityEngine.InputSystem;public AnimatorStateInfo stateInfo;void State(){//stateInfo animator.GetCurrentAnimatorStateIn…

AcrelEMS-MH民航机场智慧能源管平台解决方案【可靠供电/降低能耗/高效运维】

民航机场行业背景 自2012年以来&#xff0c;我国民航运输规模出现了显著增长&#xff0c;旅客运输量&#xff1a;从2012年的3.19亿人次上升至2019年的6.6亿人次&#xff08;注&#xff1a;为剔除疫情影响&#xff0c;此处采取疫情前2019年的数据&#xff0c;下同&#xff09;&…

数据结构七:线性表之链式栈的设计

在上篇博客&#xff0c;学习了用数组实现链的顺序存储结构&#xff0c;那是否存在用单链表实现栈的链式存储结构&#xff0c;答案是当然的&#xff0c;相比于顺序栈&#xff0c;用数组实现的栈效率很高&#xff0c;但若同时使用多个栈&#xff0c;顺序栈将浪费很多空间。用单链…

用NuGet安装 Oracle ODP.NET

oracle官网原文&#xff1a;Using NuGet to Install and Configure Oracle Data Provider for .NET Using NuGet to Install and Configure Oracle Data Provider for .NET In this section, you will install ODP.NET NuGet packages from nuget.org. Select View > Solut…

PDF 正确指定页码挂载书签后,书签页码对不上

这个问题与我的另一篇中方法一样 如何让一个大几千页的打开巨慢的 PDF 秒开-CSDN博客 https://blog.csdn.net/u013669912/article/details/138166922 另做一篇原因是一篇文章附带一个与该文章主题不相关的问题时&#xff0c;不利于被遇到该问题的人快速搜索发现以解决其遇到的…

8_手眼标定总结_auboi5机械臂与海康平面相机

经过不断地学习与调试&#xff0c;不断地学习网络上其他同志分享的资料&#xff0c;opencv手眼标定迎来了阶段性结束。实际测试结果在机械臂坐标系中X方向差5mm左右。 代码参考《https://blog.csdn.net/wanggao_1990/article/details/81435660》 注意事项&#xff1a; ①标定…

AG32 MCU在触摸屏的应用(AGM FPGA/MCU行业应用)

传统的屏驱MCU常见应用于洗衣机、空调、空调面板、仪器仪表等人机交互界面显示场景中&#xff0c;通常是以段码的形式显示设备运转的时间、温度、测量结果等简单运行数据&#xff0c;随着人机交互需求丰富化&#xff0c;智能家居设备、摩托车、电动车等产品也逐步增加了屏幕显示…

如何在 Ubuntu 12.04 上使用 Apache 配置 WebDAV 访问

简介 WebDAV 是内置在 HTTP 中的分布式网络编辑实现&#xff0c;允许您轻松共享文件并与他人协作。 我们可以在 Web 服务器中安装此扩展&#xff0c;以允许通过 Web 浏览器远程读写访问本地文件。在本指南中&#xff0c;我们将在带有 Apache Web 服务器的 Ubuntu 12.04 VPS 上…

【小沐学Java】VSCode搭建Java开发环境

文章目录 1、简介2、安装VSCode2.1 简介2.2 安装 3、安装Java SDK3.1 简介3.2 安装3.3 配置 4、安装插件Java Extension Pack4.1 简介4.2 安装4.3 配置 结语 1、简介 2、安装VSCode 2.1 简介 Visual Studio Code 是一个轻量级但功能强大的源代码编辑器&#xff0c;可在桌面上…

全志ARM-超声波测距

超声波测距模块是用来测量距离的一种产品&#xff0c;通过发送和收超声波&#xff0c;利用时间差和声音传播速度&#xff0c; 计算出模块到前方障碍物的距离 1.测距原理&#xff1a; 给Trig端口至少10us的高电平发送声波&#xff0c;Echo信号&#xff0c;由低电平跳转到高电平…

Docker 部署与操作

一 国内&#xff1a; 中国电信天翼云 提供包括云主机在内的全方位云计算服务&#xff0c;侧重于安全合规和企业级服务。 利用电信的网络优势&#xff0c;提供稳定可靠的基础设施服务。 中国联通沃云 提供包括云主机在内的多项云计算服务&#xff0c;适合不同行业和场景。 …

Redis篇:缓存雪崩及解决方案

1.何为缓存雪崩 缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机&#xff0c;导致大量请求到达数据库&#xff0c;带来巨大压力。 2.缓存雪崩的解决方案 解决方案&#xff1a; 给不同的Key的TTL添加随机值 利用Redis集群提高服务的可用性 给缓存业务添加降级…

如何避免被恶意攻击的IP地址

随着互联网的普及和发展&#xff0c;网络安全问题日益受到关注&#xff0c;恶意攻击成为网络安全的一大威胁。而IP地址作为网络通信的基础&#xff0c;常常成为恶意攻击的目标之一。本文将探讨如何避免被恶意攻击的IP地址&#xff0c;提高网络安全水平。 1. 定期更新安全补丁 …

【C++】--------模板进阶

目录 前言 一、非类型模板参数 定义 二、模板的特化&#xff08;步骤都一样&#xff09; 1.概念 2.函数模板特化的步骤 3.类模板的特化 3.1全特化 3.2偏特化/半特化 三、模板的分离与编译 1.什么是分离编译&#xff1f; 2.模板的分离与编译 四、总结 前言 我们已经…
最新文章