算法学习笔记(8)-动态规划基础篇

news2024/9/23 0:39:23

目录

基础内容:

动态规划:

动态规划理解的问题引入:

解析:(暴力回溯)

代码示例:

暴力搜索:

Dfs代码示例:(搜索)

暴力递归产生的递归树:

记忆化搜索:

代码示例:

动态规划:

代码示例:(动态规划,从最小子问题开始)

执行过程(动态规划):

解析:(动态规划)

空间优化:

代码示例:

解析:


基础内容:

什么是动态规划,动态规划作为一种手段可以解决哪些问题,动态规划的分类,以及具体的分类可以解决的具体问题的分类。

动态规划:

是一个重要的算法范式,它将一个问题分解成一系列更小的子问题,并通过存储子问题解避免重复计算,从而大幅度提升时间效率。

动态规划理解的问题引入:

通过爬楼梯的案例来引入这个问题,给定一个共有n阶的楼梯,你每步可以上1阶或者2阶,请问有多少种方案可以爬到楼顶。

解析:(暴力回溯)

本题目的目标是求解方案数量,我们可以考虑通过回溯来穷举所有可能性。具体来说,将爬楼梯想象为一个多轮选择的过程:从地面出发,每轮选择上一阶或者二阶,每当达到楼梯顶部时就将方案数量加1,当越过楼梯顶部就将其剪枝。

代码示例

# python代码示例
def backrack(choices,state,n,res) :
    if state == n :
        res[0] += 1 
    for choice in choices :
        if state + choice > n :
            continue
        backrack(choices,state+choice,n,res)
def climbing_stairs_backrack(n) :
    choices = [1,2]
    state = 0
    res = [0]
    backrack(choices,state,n,res)
    return res[0]
n = int(input())
print(climbing_stairs_backrack(n))
// c++代码示例
void backrack(vector<int> &choices, int state, int n, vector<int> &res)
{
    if (state == n )
    {
        res[0]++ ;
    }
    for (auto &choice : choices)
    {
        if (state + choice > n)
        {
            continue ;
        }
        backrack(choices, state + choice, n, res)
    }
}

int climbingStairsBackrack(int n)
{    
    vector<int> choices = {1 , 2 } ;
    int state = 0 ;
    vector<int> res = [0] ;
    backrack(choices, state, n, res) ;
    return res[0] ;
}

暴力搜索:

回溯算法通常并不显式地对问题进行拆解,而是将问题看作一系列决策步骤,通过试探和剪枝,搜索所有可能的解。

我们可以尝试从问题分解的角度分析这道题。设爬到第i阶共有dp[i]中方案,那么dp[i]就是原问题,其子问题包括:

dp[i-1],dp[i-2],dp[1],dp[2]

由于每轮只能上1阶或者2阶,因此当我们站在第i阶楼梯上时,上一轮只可能站在第i-1或者i-2台阶上。换句话说,我们只能从第i-1阶或者第i-2阶迈向第i阶。

由此便可以得出一个重要的推论:爬到第i-1阶的方案加上爬到第i-2阶的方案数就等于爬到第i阶的方案数。公式如下:

dp[i] = dp[i-1] + dp[i-2]

这就意味着,爬楼问题中存在着递推的关系,原问题可由子问题的解构建来得到解决

Dfs代码示例:(搜索)

# python 代码示例
def dfs(i : int) -> int :
    if i == 1 or i == 2 :
        return i
    count = dfs(i - 1) + dfs(i - 2)
    return count
def climbing_stairs_dfs(n : int) -> int :
    retunr dfs(n)
// c++ 代码示例
int dfs(int i)
{
    if (i == 1 || i == 2)
    {
        return i ;
    }
    int count = dfs(i - 1) + dfs(i - 2);
    return count ;
}
int climbingStairsDFS(int n)
{
    retunr dfs(n) ;
}

暴力递归产生的递归树

解决上述递归树中的重复问题,采用记忆化搜索的方式,可以把大量重复构建的相同子树进行去掉,从而达到提高计算效率。(重叠子问题

记忆化搜索:

将所有重叠的子问题只进行一遍计算,需要声明一个数组nem来记录每个子问题的解,并在搜索过程中将重叠子问题剪枝。

  1. 当首次计算dp[i]时,将其记录在nem[i],便于后续的使用
  2. 当再次计算dp[i]时,直接在nem[i]中进行获取结果,避免重复子问题的计算。

代码示例:

# python 代码示例
def dfs(i : int, mem : list[int]) -> int :
    if i == 1 or i == 2 :
        return i
    if mem[i] != -1 :
        return mem[i]
    count = dfs(i - 1, mem) + dfs(i - 2, mem)
    # 记录dfs(i)
    mem[i] = count
    return count
def climbing_stairs_dfs_mem(n : int) -> int :
    mem = [-1] * (n + 1)
    return dfs(n, mem)
// c++ 代码示例
int dfs(int i, vector<int> &mem)
{
    if (i == 1 || i == 2)
    {
        return i ;
    }
    if (mem != -1)
    {
        return mem[i] ;
    }
    int count = dfs(i - 1, mem) + dfs(i - 2, mem) ;
    mem[i] = count ;
    return count ;
}
int climbingStairsDFSMem(int n)
{
    vector<int> mem(n + 1, -1) ;
    return dfs(n, mem) ; 
}

经过记忆化处理后,所有重叠的子问题都只计算一次,时间复杂度优化到了O(n)

动态规划:

记忆化搜索是一种”从顶至低”的方法,我们从原问题(根节点)开始,递归地将较大子问题分解成较小子问题,直至解已知的最小子问题(叶节点)。之后,通过回溯逐层收集子问题的解,构建出原问题的解。

与之相反,动态规划是一种“从底至顶”方法:从最小子问题的解开始,迭代地构建更大子问题的解,直至得到原问题的解。

由于动态规划不包含回溯过程,因此只需要使用循环迭代实现,无须使用递归。

代码示例:(动态规划,从最小子问题开始)

# python 代码示例
def clibing_stairs_dp(n) :
    if n == 1 or n == 2 :
        return n
    dp = [0] * (n + 1)
    dp[1], dp[2] = 1, 2
    for i in range(3,n + 1) :
        dp[i] = dp[i-1] + dp[i- 2]
    return dp[n]
// c++ 代码示例

int climbingStairsDP(int n) 
{
    if (n == 1 || n == 2)
    {
        retunr n ;
    }
    vector<int> dp(n + 1, -1) ;
    dp[1] = 1 ;    
    dp[2] = 2 ;
    for (int i = 3 ; i <= n ; i++)
    {
        dp[i] = dp[i - 1] + dp[i- 2] ;
    }
    return dp[n] ;
}

执行过程(动态规划):

解析:(动态规划)

相似于回溯算法,动态规划也使用“状态”概念来表示问题求解的特定阶段,每个状态都对应一个子问题以及相应的局部最优解。例:爬楼梯问题的状态定义为当前所在楼梯的阶数i

根据以上内容,我们可以总结为动态术语的常用术语:

  1. 将数组dp称为{dp表},dp[i]表示状态i对应子问题的解
  2. 将最小子问题对应的状态,(第一阶和第二阶楼梯)称为初始状态
  3. 将递推公式dp[i] = dp[i-1] + dp[i-2]称为状态方程

空间优化:

dp[i] 只跟 dp[i-1] 和 dp[i-2] 有关

无须使用一个数组来存储所有子问题的解,只需要两个变量滚动前进即可。

代码示例:

# python 代码示例
def clibing_stairs_dp_comp(n) :
    if n == 1 or n == 2 :
        return n
    a, b = 1, 2
    for _ in range(3, n + 1) :
        a, b = b , a + b
    return b
// c++ 代码示例
int climbingStairsComp(int n) 
{
    if (n == 1 || n == 2)
    {
        return n ;
    }
    int a = 1 , b = 2 ;
    for (int i = 3 ; i <= n ; i++)
    {
        int temp = b ;
        b = a + b ;
        a = temp ;
    }
    return b ;
}

解析:

省去了数组dp所占用的空间,空间复杂度由O(n)降为O(1)

在动态规划问题中,当前状态仅与前面有限个状态有关,这时我们可以只保留必要的状态,通过“降维”来节省内存空间。这种空间优化技巧被称为“滚动变量”或“滚动数组”。

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

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

相关文章

easily-openJCL 让 Java 与显卡之间的计算变的更加容易!

easily-openJCL 让 Java 与显卡之间的计算变的更加容易&#xff01; 开源技术栏 本文介绍了关于在 Java 中 easily-openJCL 的基本使用&#xff01;&#xff01;&#xff01; 目录 文章目录 easily-openJCL 让 Java 与显卡之间的计算变的更加容易&#xff01;目录 easily-op…

【ARMv8/v9 GIC 系列 2.4 -- GIC SGI 和 PPI 中断的启用配置】

请阅读【ARM GICv3/v4 实战学习 】 文章目录 GIC SGI 和 PPI 中断的使能配置GICR_ISENABLER0 操作使用举例SummaryGIC SGI 和 PPI 中断的使能配置 GICR_ISENABLER0寄存器(中断设置-使能寄存器0)用于启用相应的SGI(软件生成中断)或PPI(专用外设中断)向CPU接口的转发。每个…

Vue3中drawer组件无法重新回显数据

不做drawer的时候数据是可以正常回显的&#xff0c;点击详情id是正常传值的&#xff0c;但是使用了drawer组件以后发现只会调用一次详情功能&#xff0c;以后不管点击哪条信息都不会刷新信息永远都是第一条的信息&#xff0c;但是id刷新成功了&#xff0c;后来发现是没有加v-if…

HTML5新增的input元素类型:number、range、email、color、date等

HTML5 大幅度地增加与改良了 input 元素的种类&#xff0c;可以简单地使用这些元素来实现 HTML5 之前需要使用 JavaScript 才能实现的许多功能。 到目前为止&#xff0c;大部分浏览器都支持 input 元素的种类。对于不支持新增 input 元素的浏览器&#xff0c;input 元素被统一…

数据库容灾 | MySQL MGR与阿里云PolarDB-X Paxos的深度对比

开源生态 众所周知&#xff0c;MySQL主备库&#xff08;两节点&#xff09;一般通过异步复制、半同步复制&#xff08;Semi-Sync&#xff09;来实现数据高可用&#xff0c;但主备架构在机房网络故障、主机hang住等异常场景下&#xff0c;HA切换后大概率就会出现数据不一致的问…

动感剧场设计师:打造流畅而生动的三维动画和特效

三维画图软件是设计领域必不可少的工具&#xff0c;它可以创建非常精确的三维模型&#xff0c;能够帮助设计师直观感受产品的外观&#xff0c;随时进行编辑和调整。与传统的三维画图软件相比&#xff0c;的三维画图软件无需进行安装步骤&#xff0c;节省时间又节省内存。本文将…

docker安装以及简单使用

如何安装安装 yum install -y yum-utils yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 列出可用的版本 yum list docker-ce.x86_64 --showduplicates | sort -r yum install -y docker-ce-23.0.6-1.el8 #开机自动启动 …

高创新 | CEEMDAN-VMD-GRU-Attention双重分解+门控循环单元+注意力机制多元时间序列预测

目录 效果一览基本介绍模型设计程序设计参考资料 效果一览 基本介绍 高创新 | CEEMDAN-VMD-GRU-Attention双重分解门控循环单元注意力机制多元时间序列预测 本文提出一种基于CEEMDAN 的二次分解方法&#xff0c;通过样本熵重构CEEMDAN 分解后的序列&#xff0c;复杂序列通过VMD…

BFS:边权相同的最短路问题

一、边权相同最短路问题简介 二、迷宫中离入口最近的出口 . - 力扣&#xff08;LeetCode&#xff09; class Solution { public:const int dx[4]{1,-1,0,0};const int dy[4]{0,0,1,-1};int nearestExit(vector<vector<char>>& maze, vector<int>& e…

思路打开!腾讯造了10亿个角色,驱动数据合成!7B模型效果打爆了

世界由形形色色的角色构成&#xff0c;每个角色都拥有独特的知识、经验、兴趣、个性和职业&#xff0c;他们共同制造了丰富多元的知识与文化。 所谓术业有专攻&#xff0c;比如AI科学家专注于构建LLMs,医务工作者们共建庞大的医学知识库&#xff0c;数学家们则偏爱数学公式与定…

p11函数和递归

递归与迭代 求n的阶乘。&#xff08;不考虑溢出&#xff09; int Fac1(int n) {int i0;int ret1;for(i1;i<n;i){ret*i;}return ret; } int main(){//求n的阶乘int n0;int ret0;scanf("%d",&n);retFac1(n);printf("%d\n",ret);return 0; } int Fac…

一.2.(5)共射、共集、共基三种基本放大电路的静态及动态分析;

共什么的问题&#xff1a;共什么取决于输入输出&#xff0c;共剩下的那一极 1.基本共射放大电路 见前面章节&#xff0c;不做累述 2.基本共集放大电路 列KVL方程&#xff0c;求解 AU1&#xff0c;所以又叫射极跟随器 Ib是流入基极的电流&#xff0c;Ii是从输入交流信号源流出的…

昇思25天学习打卡营第11天|文本解码原理-以MindNLP为例

文本解码原理-以MindNLP为例 这篇主要讲讲文本生成的几个方法&#xff0c;首先介绍一下什么是自回归语言模型。 自回归语言模型 autoregressive language model&#xff0c;根据前面的词或上下文&#xff0c;生成后续的词或句子的语言模型。 有几种典型的自回归语言模型&…

python爬虫入门(三)之HTML网页结构

一、什么是HTML 1、网页的三大技术要素&#xff1a; HTML定义网页的结构和信息&#xff08;骨架血肉&#xff09;CSS定义网页的样式&#xff08;衣服&#xff09;JavaScript定义用户和网页的交互逻辑&#xff08;动作&#xff09; 2、一个最简单的HTML&#xff1a;用<>…

动态数据库设计

动态数据库设计是一种灵活的方法&#xff0c;用于构建能够适应不断变化的数据需求的数据库结构。它强调在不频繁修改数据库表结构的前提下&#xff0c;有效管理和存储多样化的数据。以下是实现动态数据库设计的一些关键技术点和策略&#xff1a; 实体-属性-值&#xff08;EAV&a…

意得辑ABSJU202优惠15%啦,新用户注册直减哦

不得不说&#xff0c;还得是意得辑&#xff0c;钱不白花&#xff0c;润色的挺好~ 第一篇SCI终于成功见刊&#xff01;&#xff01;&#xff01; 都来接accept&#xff01;&#xff01;&#xff01;谢谢accept小狗&#xff0c;接accept 求求accept小狗&#xff0c;真的想要双证毕…

OpenLayers对要素进行新增绘制、选择、修改等交互操作

1、绘制-Draw 新建一个用来绘制要素的图层&#xff1a; const vector new VectorLayer({source: new VectorSource(),style: {"fill-color": "rgba(255, 255, 255, 0.2)","stroke-color": "#ffcc33","stroke-width": 2,&q…

如何提升美国Facebook直播的整体体验?

Facebook作为全球最大的社交媒体平台之一&#xff0c;提供了直播功能&#xff0c;用户可以实时分享生活、见解和创意。许多商家通过美国Facebook直播来获取更多客户&#xff0c;但直播时可能会遇到网络卡顿的问题&#xff0c;导致观看体验不佳。本文将探讨如何解决这个问题&…

Ubuntu开源软件LibreOffice将Excel多表转PDF多目录示例

一、实现的起因&#xff1a; Windows平台下&#xff0c;常见的WPS办公自动化套件中电子表格软件&#xff0c;其中具备将Excel工作表中数据转为PDF文档表格的功能。现在进一步的需求是&#xff1a;像PDF标准的电子书那样&#xff0c;具备一本书的目录结构或章节结构&#xff0c…

[C++]入门基础(1)

Hello大家好&#xff0c;今天通过本篇文章&#xff0c;我们来初步学习C&#xff0c;C可以说是对C语言的一个升级&#xff0c;我们会一步一步的由浅入深的学习C。 目录 1.第一个C程序 2.命名空间 2.1 命名空间出现的意义 2.2 namespace的定义 2.3 命名空间的使用 3.C输入…