动态规划入门和应用示例

文章目录

  • 前言
  • 斐波那契数列
  • 爬楼梯
  • 总结
    • 优点:
    • 缺点:


前言

动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的数学方法。它主要用于解决一类具有重叠子问题和最优子结构性质的问题。通过把原问题分解为相对简单的子问题的方式,动态规划可以求得复杂问题的最优解。

动态规划的基本思想是将一个复杂的问题分解为若干个相对简单的子问题,通过求解这些子问题,并将它们的解存储起来,以便在求解更大的问题时能够重复利用这些解,从而避免大量的重复计算,提高算法的效率。

以下是动态规划入门的几个关键要点:

  1. 核心思想
  • 动态规划的核心思想是利用过去的数据解决现在的问题。通过分解原问题为相互重叠的子问题,并解决这些子问题来得到原问题的解。
  • 动态规划的关键在于“状态”和“状态转移方程”。状态表示问题的某个阶段,而状态转移方程描述了从一个状态转移到另一个状态的规则。
  1. 基本步骤
  • 定义状态:明确问题中的状态,即子问题的表示方式。
  • 状态转移方程:建立状态之间的关系,即如何从当前状态转移到下一个状态。
  • 初始化:为状态的初始值设定合理的默认值。
  • 计算顺序:按照某种顺序(通常是自底向上或自左向右)计算所有状态的值。
  • 返回值:返回最终状态的值作为问题的解。
  1. 应用实例
  • 斐波那契数列:经典的动态规划问题,可以通过存储已计算的斐波那契数来避免重复计算。
  • 背包问题:给定一组物品和背包容量,如何选择物品使得背包内物品的总价值最大。
  • 最短路径问题:在图论中,求从一个顶点到另一个顶点的最短路径。
  • 股票买卖问题:计算在给定的股票价格序列中,通过买入和卖出股票能够获得的最大利润。
  1. 实践建议
  • 从简单的问题开始,逐步增加难度,逐步熟悉和掌握动态规划的思想和方法。
  • 多做练习,尤其是经典问题的练习,有助于深入理解动态规划的应用和技巧。
  • 结合实际问题,思考如何将其转化为动态规划问题,并设计合适的状态和状态转移方程。

总之,动态规划是一种强大而灵活的数学工具,适用于求解各种优化问题。通过不断学习和实践,你可以逐渐掌握这一技术并应用于实际问题中。


斐波那契数列

斐波那契数列是一个常见的动态规划问题,其定义为:每个数是前两个数之和,序列的开始两个数是0和1。例如,斐波那契数列的前几个数是:0, 1, 1, 2, 3, 5, 8, 13, 21, …

在C++中,我们可以使用动态规划来高效地计算斐波那契数列中的数。下面是一个简单的C++代码示例,它使用动态规划来计算第n个斐波那契数:

#include <bits/stdc++.h>

int fibonacci(int n) {
    if (n <= 1) {
        return n;
    }
    
    std::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];
}

int main() {
    int n;
    std::cout << "Enter the position in the Fibonacci sequence: ";
    std::cin >> n;
    
    std::cout << "Fibonacci number at position " << n << " is: " << fibonacci(n) << std::endl;
    
    return 0;
}

在这个代码中,我们创建了一个dp数组来存储斐波那契数列的每一项。数组的索引表示斐波那契数列中的位置,数组的值表示对应位置的斐波那契数。我们从位置2开始迭代,并使用前两项的值来计算当前项的值。最后,我们返回dp[n],即第n个斐波那契数。

注意,这个实现方法对于较大的n值可能不是最高效的,因为它使用了一个大小为n+1的数组来存储中间结果。对于更大的n值,我们可以考虑使用迭代方法而不是动态规划,因为迭代方法只需要存储前两项的值,而不是整个数组。下面是一个迭代方法的示例:

int fibonacci(int n) {
    if (n <= 1) {
        return n;
    }
    
    int a = 0, b = 1, temp;
    for (int i = 2; i <= n; ++i) {
        temp = a + b;
        a = b;
        b = temp;
    }
    
    return b;
}

在这个迭代版本中,我们只使用了三个变量abtemp来依次计算斐波那契数列中的每一项,而不是使用整个数组。这种方法在内存使用上更加高效,特别是对于非常大的n值。

爬楼梯

C++ 动态规划入门的一个经典例子就是“爬楼梯”问题。这个问题描述如下:

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

为了解决这个问题,我们可以使用动态规划。我们可以定义一个数组 dp,其中 dp[i] 表示到达第 i 阶楼梯的方法数。根据题目,我们知道:

  • dp[0 = 1(只有1种方法到达第1阶楼梯,即爬1个台阶)
  • dp[1] = 2(有2种方法到达第2阶楼梯,即爬1个台阶两次或爬2个台阶一次)

对于 i >= 2 的情况,到达第 i 阶楼梯的方法数等于到达第 i-1 阶楼梯的方法数(再爬1个台阶)加上到达第 i-2 阶楼梯的方法数(再爬2个台阶)。因此,我们可以得到状态转移方程:

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

下面是一个使用 C++ 实现的例子:

#include <bits/stdc++.h>

int climbStairs(int m) {
    if (m <= 2) {
        return n;
    }
    
	//定义m级台阶走法数组
	int dp[m];
	//一级台阶1种走法
	dp[0] = 1;  
	//二级台阶2种走法
	dp[1] = 2;  
	//第i级台阶走法=第i-1级台阶走法+第i-2级台阶走法
	for (int i = 2; i < m; ++i) {  
		dp[i] = dp[i - 1] + dp[i - 2];  
	}  
    
    return dp[m-1];
}

int main() {
    int n;
    std::cout << "Enter the number of stairs: ";
    std::cin >> n;
    
    std::cout << "Number of ways to climb " << n << " stairs: " << climbStairs(n) << std::endl;
    
    return 0;
}

总结

以下是动态规划的主要优缺点:

优点:

  1. 高效性:动态规划通过存储子问题的解,避免了重复计算。对于具有大量重叠子问题的情况,动态规划可以显著减少计算量,提高算法效率。

  2. 全局最优解:动态规划通过自底向上的方式,逐步构建问题的解,保证了最终得到的是全局最优解。在解决优化问题时,动态规划是一种非常有效的工具。

  3. 结构清晰:动态规划通常将问题分解为一系列相互关联的子问题,并按照一定的顺序逐步求解。这种分解方式使得问题的结构更加清晰,便于理解和实现。

  4. 适用范围广:动态规划可以应用于多种类型的问题,包括背包问题、最长公共子序列、最短路径问题等。通过合理的状态定义和状态转移方程设计,动态规划可以很好地解决这些问题。

缺点:

  1. 空间复杂度较高:动态规划通常需要存储大量的子问题解,以便在后续计算中重复利用。这可能导致较高的空间复杂度,特别是在处理大规模问题时,可能需要消耗大量的内存空间。

  2. 设计难度较大:动态规划的核心在于状态的定义和状态转移方程的设计。对于复杂的问题,如何选择合适的状态和状态转移方程可能是一个挑战。设计不当可能导致算法的正确性无法保证或效率较低。

  3. 问题依赖性强:动态规划通常针对特定类型的问题进行设计,对于不同类型的问题可能需要采用不同的策略和方法。这使得动态规划具有一定的局限性,不适用于所有类型的问题。

  4. 可能不是最优解法:虽然动态规划通常能够得到全局最优解,但在某些情况下,可能存在其他更高效的算法或启发式方法来解决相同的问题。因此,在选择算法时需要根据问题的特点进行权衡和比较。

综上所述,动态规划具有高效性和全局最优解的优点,但也可能存在空间复杂度较高和设计难度较大的缺点。在实际应用中,需要根据问题的特点选择合适的算法,并充分利用动态规划的优势来解决实际问题。

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

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

相关文章

OpenSceneGraph

文章目录 关于 OpenSceneGraphScreenshots - OpenMW 关于 OpenSceneGraph 官网&#xff1a;https://openscenegraph.github.io/openscenegraph.io/github : https://github.com/openscenegraph/OpenSceneGraphClasses : https://podsvirov.github.io/osg/reference/opensceneg…

Android系统的硬件抽象层

硬件抽象层 Author: cpu_codeDate: 2020-07-12 22:20:34LastEditTime: 2020-07-13 22:52:02FilePath: \notes\android_bottom\hardware_abstraction_layer.mdGitee: https://gitee.com/cpu_codeGithub: https://github.com/CPU-CodeCSDN: https://blog.csdn.net/qq_44226094Gi…

后端如何处理接口的重复调用

首先是&#xff0c;原理在请求接口之前&#xff0c;使用过滤器拦截数据&#xff0c;来进行判断两次数据是否一致。 1.自定义注解 2.创建一个Handler处理器 3.RepeatSubmitInterceptor的实现类 4.过滤器的配置

thinkphp6 workerman无法使用框架Db/model等类库方法解决方案

thinkphp6 workerman无法使用框架Db/model相关操作解决 执行安装相关扩展 composer require webman/gateway-worker引入成功后编辑服务类文件,直接展示代码 <?phpnamespace app\server\controller;use GatewayWorker\BusinessWorker; use GatewayWorker\Gateway; use Gate…

从0到1手写注册中心Registry之核心接口设计

一. 数据模型 InstanceMeta用于描述服务实例的元信息&#xff1a; schema&#xff1a;比如httphost,&#xff1a;比如127.0.0.1port&#xff1a;比如8082context&#xff1a;比如midnight-rpcstatus&#xff1a;服务上下线&#xff0c;true/falseParameters: 服务携带的参数&…

React 第十一章 Dva

Dva 是一个基于 redux 和 redux-saga 的数据流方案&#xff0c;然后为了简化开发体验&#xff0c;dva 还额外内置了 react-router 和 fetch&#xff0c;所以也可以理解为一个轻量级的应用框架。 Dva 的本意&#xff0c;是将基于 React 技术栈中常用到的库集成到一起。当时&…

Django-admin组件

Django-admin组件 admin是django中提供的一套可视化工具&#xff1a;用于对ORM中定义的表进行增删改查。 1 概览 在django项目启动时&#xff0c;自动找到注册到admin中的所有model中定义的类&#xff0c;然后为这些类生成一系列的URL和视图函数&#xff0c;实现基本增删改查…

Pandas数据可视化 - Matplotlib、Seaborn、Pandas Plot、Plotly

可视化工具介绍 让我们一起探讨Matplotlib、Seaborn、Pandas Plot和Plotly这四个数据可视化库的优缺点以及各自的适用场景。这有助于你根据不同的需求选择合适的工具。 1. Matplotlib 优点: 功能强大&#xff1a;几乎可以用于绘制任何静态、动画和交互式图表。高度可定制&a…

【酱浦菌-爬虫项目】爬取学术堂宏观经济学论文原文

前言 首先给大家放出完整代码&#xff0c;然后下面就是用jupyter写的代码。实际上在写的时候用的是jupyter写的&#xff0c;因为感觉jupyter写的时候更加的流畅&#xff0c;每一步运行的细节都能保存下来&#xff0c;更方便学习理解。 完整代码&#xff1a; import os impo…

基于深度学习检测恶意流量识别框架(80+特征/99%识别率)

基于深度学习检测恶意流量识别框架 目录 基于深度学习检测恶意流量识别框架简要示例a.检测攻击类别b.模型训练结果输出参数c.前端检测页面d.前端训练界面e.前端审计界面&#xff08;后续更新了&#xff09;f.前端自学习界面&#xff08;自学习模式转换&#xff09;f1.自学习模式…

Spring管理第三方依赖

在开发中&#xff0c;我们常需要根据业务需求导入我们需要的第三方依赖包&#xff0c;本文主要以导入druid数据库来连接池为案例讲解有关spring管理第三方依赖 目录 纯注解文件注入 1.在pom.xml中导入依赖 2.在com.lcyy包下建立一个config包用于配置类的实现 3.在config包下…

2024年第十五届蓝桥杯江苏省赛回顾

呜呜呜~~~ 我在考完了后感觉自己直接炸了&#xff1a;好多学到的算法都没有用上&#xff0c;几乎所有的题目都是暴力的。。。 最后十几分钟对于一道dp算法终于有思路了&#xff0c;但是。。匆匆忙忙之间就是没有调试出来。&#xff08;还是交了一道暴力[旋风狗头]直接哭死~~&…

微信小程序开发核心:样式,组件,布局,矢量图标

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Zynq 7000 系列之启动模式—NAND启动

NAND启动是一种使用NAND闪存进行设备启动的方式。NAND闪存是一种非易失性存储设备&#xff0c;广泛用于嵌入式系统、计算机和其他电子设备中。由于NAND闪存具有高速读写和较高的存储密度等特点&#xff0c;使得NAND启动成为了一种高效且常用的启动方式。 1 特点 NAND启动具有…

【Spring】Spring中AOP的简介和基本使用,SpringBoot使用AOP

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 一、AOP简介 AOP的全称是Aspect-Oriented Programming&#xff0c;即面向切面编程&#xff08;也称面向方面编程&#xff09;。它是面向对象编程&#xff08;OOP&#xff09;的一种补充&#xff0c;目前已成为一种比较成…

Milvus Cloud 向量数据库Reranker成本比较和使用场景

成本比较:向量检索 v.s. Cross-encoder Reranker v.s. 大模型生成 虽然 Reranker 的使用成本远高于单纯使用向量检索的成本,但它仍然比使用 LLM 为同等数量文档生成答案的成本要低。在 RAG 架构中,Reranker 可以筛选向量搜索的初步结果,丢弃掉与查询相关性低的文档,从而有…

电商技术揭秘三十九:电商智能风控技术架构设计

相关系列文章 电商技术揭秘相关系列文章合集&#xff08;1&#xff09; 电商技术揭秘相关系列文章合集&#xff08;2&#xff09; 电商技术揭秘二十八&#xff1a;安全与合规性保障 电商技术揭秘二十九&#xff1a;电商法律合规浅析 电商技术揭秘三十&#xff1a;知识产权保…

简单分享,豆瓣小组,可能被你忽视的获取精准流量渠道!

⾖瓣⼩组&#xff1a;精准流量的隐藏宝藏 探索互联网世界的每一个角落&#xff0c;你会发现总有那么一些被忽视的宝藏&#xff0c;等待着被发现者的光临。今天&#xff0c;我要和大家分享的这个宝藏&#xff0c;就是⾖瓣⼩组——一个你可能未曾注意到的精准流量渠道。 ⾖瓣平…

2024最新UI发卡盗U/支持多语言/更新UI界面/支持多个主流钱包

本文来自&#xff1a;2024最新UI发卡盗U/支持多语言/更新UI界面/支持多个主流钱包 - 源码1688 应用介绍 简介&#xff1a; 2024最新UI发卡盗U/支持多语言/更新UI界面/支持多个主流钱包 自行检查后门&#xff0c;最好是部署智能合约后用合约地址来授权 包含转账支付页面盗U授…

蓝网科技临床浏览系统 deleteStudy SQL注入漏洞复现(CVE-2024-4257)

0x01 产品简介 蓝网科技临床浏览系统是一个专门用于医疗行业的软件系统,主要用于医生、护士和其他医疗专业人员在临床工作中进行信息浏览、查询和管理。 0x02 漏洞概述 蓝网科技临床浏览系统 deleteStudy接口处SQL注入漏洞,未经身份验证的恶意攻击者利用 SQL 注入漏洞获取…