算法——动态规划:基础

news2024/11/15 4:25:14

文章目录

  • 一、基本介绍
  • 二、案例——斐波那契数列
    • 1. 基本介绍
    • 2. 递归实现
    • 3. 动态规划
      • 3.1 重叠子问题
      • 3.2 最优子结构
      • 3.3 无后效性
      • 3.4 性质的总结
    • 4. 使用 动态规划 的思想实现
      • 4.1 自顶向下 的 递归
      • 4.2 自底向上 的 递推
      • 4.3 两种思路的简单比较
  • 三、总结


一、基本介绍

动态规划(Dynamic Programming,DP)是求解 多阶段决策 问题 最优化 的一种算法思想,它将 大问题分解成更简单的子问题,对 整体问题的最优解 取决于 子问题的最优解。用于解决具有 重叠子问题最优子结构 特征的问题。关于 重叠子问题最优子结构,在案例中会进行介绍。

二、案例——斐波那契数列

1. 基本介绍

斐波那契数列 是一个 递推数列,它的每个数字是前面两个数字之和,如 1 , 1 , 2 , 3 , 5 , 8 ⋯ 1, 1, 2, 3, 5, 8 \cdots 1,1,2,3,5,8。其递推公式为 f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n) = f(n - 1) + f(n - 2) f(n)=f(n1)+f(n2) n ≥ 3 n \geq 3 n3 n ∈ N + n \in N_+ nN+,此外 f ( 1 ) = 1 , f ( 2 ) = 1 f(1) = 1, f(2) = 1 f(1)=1,f(2)=1。总结就是如下的公式:
f ( n ) = { 1 , n = 1 , 2 f ( n − 1 ) + f ( n − 2 ) , n ≥ 3 , n ∈ N + f(n) = \begin{cases} 1, n = 1, 2 \\ f(n - 1) + f(n - 2), n \geq 3 \end{cases} , n \in N_+ f(n)={1,n=1,2f(n1)+f(n2),n3,nN+

2. 递归实现

递归的实现很简单,只看递推公式就可以了。代码如下:

int f(int n) {
	if (n == 1 || n == 2) {
		return 1;
	}
	return f(n - 1) + f(n - 2);
}

可以发现,每计算一个总问题 f ( n ) f(n) f(n),就需要计算两个子问题 f ( n − 1 ) , f ( n − 2 ) f(n - 1), f(n - 2) f(n1),f(n2),从而导致该实现的时间复杂度为 O ( 2 n ) O(2^n) O(2n),算是非常差劲的时间复杂度了,这时就需要考虑使用 动态规划 进行优化。

3. 动态规划

前面提到过,使用 动态规划 解决的问题有两个特征。

3.1 重叠子问题

重叠子问题 有两个特点:

  • 子问题是原大问题的小版本,计算步骤完全一样
  • 计算大问题时,需要多次 重复 计算小问题。

在斐波那契数列中,计算 f ( 5 ) f(5) f(5) 会被分解成以下的子问题:
alt text
可以发现, f ( 3 ) f(3) f(3) 被计算了多次,实际上只需要计算一次,保存其结果即可。

一个子问题的多次重复计算会耗费大量时间,这里就能想到一种策略——以空间换时间:保存已经计算过的子问题的结果。在之后的使用时,直接用保存的结果进行计算。这就是 动态规划 的思想,避免重复计算某个子问题,但会耗费更多的空间

使用这种策略,就可以将计算 f ( 5 ) f(5) f(5) 优化成以下的子问题:
alt text

3.2 最优子结构

最优子结构 也有两个特点:

  • 大问题的最优解 包含 小问题的最优解。
  • 可以通过小问题的最优解 推导出 大问题的最优解。

在斐波那契数列中,要计算 f ( n ) f(n) f(n),就得先计算 f ( n − 1 ) , f ( n − 2 ) f(n - 1), f(n - 2) f(n1),f(n2)。其中, f ( n ) f(n) f(n) 是大问题的最优解, f ( n − 1 ) , f ( n − 2 ) f(n - 1), f(n - 2) f(n1),f(n2) 是小问题的最优解。由于斐波那契数列比较简单,每个 n n n 只对应一个解,所以没有体现出 最优 的概念,之后的各种背包问题会有 最优 的概念。

3.3 无后效性

动态规划 中还有一个名词:无后效性,它也比较重要。

无后效性 是使用 动态规划必要条件,如果问题不满足 无后效性,则无法使用动态规划。无后效性 意味着 只关心结果,不关心过程,即只需要保存计算的结果,不需要保存计算的过程。

在斐波那契数列中,要计算 f ( n ) f(n) f(n),就得先知道 f ( n − 1 ) , f ( n − 2 ) f(n - 1), f(n - 2) f(n1),f(n2) 的结果,而不需要知道它们是如何计算的。

3.4 性质的总结

最优子结构 所具有的性质(可以通过小问题的最优解 推导出 大问题的最优解)足以说明该问题有 无后效性。只要一个问题具有 重叠子问题最优子结构 的特征,就可以使用 动态规划 的思想进行优化。

4. 使用 动态规划 的思想实现

动态规划 有两种编程思路:

  • 自顶向下(Top-Down):先大问题,再小问题。通常采用 递归 实现,也叫 记忆化搜索
  • 自底向上(Bottom-Up):先小问题,再大问题。通常采用 递推 实现。

说明:此处的“先”不代表“先解决”,而是代表“先遍历到”,无论哪种实现方式,都是先解决小问题,然后才能解决大问题,并且所有问题都只被解决一次。此外,这两种思路的时间复杂度和空间复杂度是一样的。

4.1 自顶向下 的 递归

  • 中心思想:先 考虑 大问题,再缩小到小问题,直到最小的问题,最后从小到大依次解决所有问题
  • 实现:解决完子问题后就将其结果保存起来,如果需要再次使用,直接获取保存的结果。

使用 递归 的 动态规划 的思想,实现的 斐波那契数列 如下所示:

const int N = 255; // N 是一个常数,表示数组的长度
int memoize[N]; // 用于保存结果的数组
int f(int n) {
	if (n == 1 || n == 2) {
		return 1;
	}
	if (memoize[n] != 0) { // 如果数组中保存了 f(n)
		return memoize[n]; // 则直接将其返回
	}
	// 否则计算出 f(n),先保存它,然后再返回
	memoize[n] = f(n - 1) + f(n - 2);
	return memoize[n];
}

这种方式的时间复杂度为 O ( n ) O(n) O(n)

4.2 自底向上 的 递推

  • 中心思想:先 解决 子问题,再递推到大问题
  • 实现:使用若干个 for 循环填写一维或多维数组 dpdp 的每个元素都有具体含义。
  • 注意:递推 避免了多层递归导致的 栈溢出 问题,使用动态规划时一般采用这种思路。

使用 递推 的 动态规划 的思想,实现的 斐波那契数列 如下所示:

const int N = 255; // N 是一个常数,表示数组的长度
int dp[N]; // dp[i] 表示 f(i)
int f(int n) {
	dp[1] = dp[2] = 1; // 初始状态
	for (int i = 3; i <= n; i++) {
		dp[i] = dp[i - 1] + dp[i - 2];
	}
	return dp[n];
}

这种方式的时间复杂度也为 O ( n ) O(n) O(n)

4.3 两种思路的简单比较

  • 自顶向下 的 递归:能够更宏观地把握问题、认清问题的本质。
  • 自底向上 的 递推:编码更直接,不会造成 栈溢出 问题。

三、总结

动态规划 经常用于具有 重叠子问题最优子结构 特征的问题,使用 空间 换 时间 的思想,有 递归递推 两种实现方式,一般使用 递推 的方式,所以经常在动态规划的题解中看到 dp 数组。

本文介绍了动态规划中最基础的知识,更高级的知识(如滚动数组、背包问题等)将会在之后的文章中依次展开。

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

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

相关文章

python语言day4 函数 生成器yield、next关键字 装饰器

一、 函数 定义&#xff1a; def info(): print("执行info()函数") 直接调用方法和封装一个函数&#xff1a; 使用信息发送的功能为例 登录163网易免费邮-你的专业电子邮局 1&#xff09;开启POP3/SMTP服务&#xff0c;会得到对应的授权码&#xff0c;也就是登陆…

2024年旗舰骨传导耳机大对比:南卡、韶音和墨觉,哪款最值得购买?

作为专注于数码产品的博主&#xff0c;我对骨传导耳机的迅猛发展深有感触。这类耳机以其与众不同的技术和设计风格&#xff0c;成功捕捉了消费者的兴趣。它们独特的工作原理不仅保留了使用者对周围环境的感知&#xff0c;还能提供清晰的音乐播放和通话体验&#xff0c;特别适合…

第三方软件检测机构服务类型

在信息技术飞速发展的今天&#xff0c;软件产品的质量已成为企业竞争力的重要组成部分。卓码软件测评这家第三方软件检测机构致力于提供一流的软件测试服务&#xff0c;帮助企业确保其软件产品的可靠性和安全性。 一、项目验收测试&#xff1a;确保交付质量   项目验收测试是…

力扣-240.搜索二维矩阵(2)

刷力扣热题–第二十七天:240.搜索二维矩阵(2) 新手第二十七天 奋战敲代码&#xff0c;持之以恒&#xff0c;见证成长 1.题目简介 2.题目解答 这道题的想法就是,整体遍历,在遇到比target还大的,就停止这行的遍历,然后转过去继续遍历下一行,如果有一行的开头大于target,直接返回…

嘉盛平台的代理返佣机制:一份详细的说明书

在金融市场合作模式的多样性中&#xff0c;嘉盛平台的代理返佣机制无疑是一个引人注目的亮点。想了解更多关于嘉盛平台的代理返佣机制吗&#xff1f;本文将为您详细解答。嘉盛开户MT4平台开户链接 &#xff1a;https://application.jszhanghao.com/cn-meta/step/1?ibcodeFXAMM…

Windows--WSL2--Ubuntuon--Docker

编写目的&#xff1a; 在Windows上安装Docker&#xff0c;用Docker安装Gitlab、Jenkins等软件。 文章记录一下Windows上安装Docker的过程。 参考文档&#xff1a; 旧版 WSL 的手动安装步骤 | Microsoft Learn 下面用"参考文档"代替 目录 第一步&#xff1a;启…

RK3568平台开发系列讲解(文件系统篇)FLASH 均衡擦写(UBI)

🚀返回专栏总目录 文章目录 一、UBI均衡二、日志打印三、常见打印四、erase_worker四、ensure_wear_leveling五、wear_leveling_worker上层应用通过逻辑地址来访问存储设备,FTL把不同的逻辑地址映射到Nand Flash中的不同位置。 一、UBI均衡 Ubi擦写均衡在ubi驱动中c 处理,u…

66 函数精彩案例

1 编写函数&#xff0c;接收任意多个实数&#xff0c;返回一个元组&#xff0c;其中第一个元素为所有参数的平均值&#xff0c;其他元素为所有参数中大于平均值的实数。 def func(*parameter):avg sum(parameter) / len(parameter) # 平均值g [i for i in parameter if i &…

哈萨克语驾考学习软件求推荐?

哈语驾考APP专门为哈萨克族考驾照的学员提供了科目一科目四题库在线练习和模拟考试&#xff0c;是一款哈汉双语版本的驾考学习APP。软件内可同时切换哈萨克语题库和语言文字&#xff0c;有多种学习模式。题库同步车管所考题&#xff0c;通过率高。包含了科一、科四模拟考试、路…

PP 8 创建工艺路线

事务代码&#xff1a;CA01(注&#xff1a;定额工艺路线&#xff1a;CA21(重复制造)) 组和组计数器确定唯一工艺路线 创建一个组 把组分配给物料 物料有多个工艺路线 可以把组分给多个物料 如果打上删除标识&#xff0c;工艺路线无效

前端(四):前后端分离开发(YAPI的使用)

一、引入 1、前后端混合开发&#xff08;早期&#xff09;&#xff1a;将前端、后端、数据库混杂在一起写&#xff0c;前后前全部在一个工程中。沟通成本高、分工不明确、不便于管理、不便于维护和扩展。 2、前后端分离开发&#xff1a;前端工程和后端工程&#xff0c;为了前…

设计模式之拦截器模式

目录 1.概述 2.结构 3.java实现示例 4.常见实现框架 5.C实现拦截器模式 6.拦截器和过滤器的异同 7.应用场景 8.优缺点 9.总结 1.概述 拦截器模式&#xff08;Interceptor Pattern&#xff09;是一种在请求被处理之前或之后自动执行代码的设计模式。它允许开发者在方法…

【自动驾驶】ubuntu20.04安装完整ROS的Noetic版本

目录 安装过程换源及安装注意&#xff1a;三级目录 安装过程 1.配置ubuntu的软件和更新 配置ubuntu的软件和更新&#xff0c;允许安装不经认证的软件。 首先打开“软件和更新”对话框&#xff0c;在设置菜单中的&#xff0c;关于&#xff0c;软件更新。 打开后按照下图进行配…

字符串左旋(c语言)

1.字符串左旋 //实现一个函数&#xff0c;可以左旋字符串的k个字符 例如&#xff1a;ABCD左旋字符串的1个字符BCDA ABCD左旋字符串的2个字符CDAB 2.第一步我们先输入k&#xff08;scanf&#xff09;,将第一位进行储存&#xff0c;然后其他位先前走一位&#xff0c;然后将第一…

分布式事务一站式解决方案-Seata

分布式事务一站式解决方案- 分布式事务一站式解决方案分布式事务产生背景三个概念Seata下载和安装实际业务模拟演示不加 GlobalTransactional 注解&#xff0c;正常操作下单不加 GlobalTransactional 注解&#xff0c;下单过程出异常或者超时了加 GlobalTransactional 注解&…

Shell定时上传日志到HDFS

Shell定时上传日志到HDFS 一、任务需求二、实现思路三、具体实现流程3.1 规划文件上传目录3.2 开发 shell 脚本3.3 授予 shell 可执行权限3.4 手动执行查看3.4 定时执行 shell 脚本 一、任务需求 公司在线服务器每天都会产生网站运行日志&#xff0c;为了避免志文件过大&#…

WPF学习笔记

WPF WPF&#xff08;Windows Presentation Foundation&#xff0c;Windows呈现基础&#xff09;是微软推出的基于Windows 的用户界面框架&#xff0c;属于.NET Framework 3.0的一部分。它提供了统一的编程模型、语言和框架&#xff0c;真正做到了分离界面设计人员与开发人员的…

学习STM32(6)-- STM32单片机ADCDAC的应用

1 引 言 深入了解并掌握STM32F103单片机在模拟数字转换&#xff08;ADC&#xff09;和数字模拟转换&#xff08;DAC&#xff09;应用方面的功能和操作。学习如何配置STM32F103的ADC模块&#xff0c;实现模拟信号到数字信号的精确转换&#xff1b;同时&#xff0c;探索DAC模块…

【AI学习】[2024北京智源大会]具身智能:具身智能关键技术研究:操纵、决策、导航

具身智能关键技术研究&#xff1a;操纵、决策、导航 董 豪 | 北京大学助理教授 依然是边看边做些记录 这张图的重点是在说&#xff0c;我们的大脑&#xff0c;也是不同的部分处理不同的功能。这里面有些功能&#xff0c;比如视觉、听觉理解等功能&#xff0c;LLM已经具备&…

一键浪漫的回忆:微软开源的修复工具!!【送源码】

项目介绍 “Bringing-Old-Photos-Back-to-Life”是一款由微软开发的创新软件解决方案&#xff0c;它利用人工智能技术来修复和增强老旧照片的质量。这款工具可以解决老旧照片中常见的问题&#xff0c;如褪色、低分辨率以及物理损坏&#xff08;如划痕和撕裂&#xff09;。通过采…