动态规划的递归写法和递推写法详解

news2025/1/15 23:24:18

目录

动态规划的概念

动态规划的递归写法

动态规划的递推写法

动态规划的概念

动态规划是一种用来解决一类最优化问题的算法思想。简单来说,动态规划将一个复杂的问题分解成若干个子问题,通过综合子问题的最优解来得到原问题的最优解。需要注意的是,动态规划会将每个求解过的子问题的解记录下来,这样当下一次碰到同样的子问题时,就可以直接使用之前记录的结果,而不是重复计算。注意:虽然动态规划采用这种方式来提高计算效率,但不能说这种做法就是动态规划的核心。

动态规划的递归写法

以斐波那契数列为例,斐波那契数列的定义为F_{0}=1,F_{1}=1,F_{n}=F_{n-1}+F_{n-2}\left ( n\geqslant 2 \right )

int F(int n){
	if(n==0||n==1){
		return 1;
	}
	else{
		return F(n-1)+F(n-2);
	}
}

为了避免重复计算,可以开一个一维数组dp,用以保存已经计算过的结果,其中dp[n]记录F(n)的结果,并用dp[n]==-1表示F(n)当前还没有被计算过。

int dp[maxn];

然后就可以在递归当中判断dp[n]是否为-1:如果不是-1,说明已经计算过F(n),直接返回dp[n]就是结果;否则,按照递归式进行递归。代码如下:

int F(int n){
	if(n==0||n==1){
		return 1;
	}
	if(dp[n]!=-1){
		return dp[n];
	}
	else{
		dp[n]=F(n-1)+F(n-2);
		return dp[n];
	}
}

这样就把已经计算过的内容记录下来,于是当下次再碰到需要计算相同的内容时,就能直接使用上次计算的结果,这可以省去大半无效计算,而这也是记忆化搜索这个名字的由来。

通过上面的例子可以引申出一个概念:如果一个问题可以被分解为若干个子问题,且这些子问题会重复出现,那么就称这个问题拥有重叠子问题。动态规划通过记录重叠子问题的解,来使下次碰到相同的子问题时直接使用之前记录的结果,以此避免大量重复计算。因此,一个问题必须拥有重叠子问题,才能使用动态规划去解决。

动态规划的递推写法

以经典的数塔问题为例,将一些数字排成数塔的形状,其中第一层有一个数字,第二层有两个数字…第n层有n个数字。现在要从第一层走到第n层,每次只能走向下一层连接的两个数字中的一个,问:最后将路径上所有数字相加后得到的和最大是多少?

针对这个问题,枚举的话时间复杂度太高。不妨令dp[i][j]表示第i行第j个数字出发的到达最底层的所有路径中能得到的最大和。在定义这个数组之后,dp[1][1]就是最终想要的答案。

同时,我们可以考虑到如果要求出dp[i][j],那么一定要先求出它的两个子问题“从位置(i+1,j)到达最底层的最大和dp[i+1][j]”和"从位置(i+1,j+1)到达最底层的最大和dp[i+1][j+1]“,即进行了一次决策:走位置(i,j)的左下还是右下。于是dp[i][j]就是dp[i+1][j]和dp[i+1][j+1]的较大值加上f[i][j]。写成式子就是:dp[i][j]=max\left ( dp[i+1][j], dp[i+1][j+1]\right )+f[i][j].

把dp[i][j]称为问题的状态,而把上面的式子称作状态转移方程,它把状态dp[i][j]转移为dp[i+1][j]和dp[i+1][j+1]。可以发现,状态dp[i][j]只与第i+1层的状态有关,而与其他层的状态无关,这样层号为i的状态总是可以由层号为i+1的两个子状态得到。那么,如果总是将层号增大可以发现数塔的最后一层的dp值总是等于元素本身,即dp[n][j]=f[n][j],把这种可以直接确定其结果的部分称为边界,而动态规划的递推写法总是从这些边界出发,通过状态转移方程扩散到整个dp数组。

这样就可以从最底层各位置的dp值开始,不断往上求出每一层各位置的dp值,最后就得到dp[1][1],即为最后得到的答案。

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1000;
int f[maxn][maxn],dp[maxn][maxn];
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=i;j++){
			cin>>f[i][j];
		}
	}
	for(int j=1;j<=n;j++){
		dp[n][j]=f[n][j];
	}
	for(int i=n-1;i>=1;i--){
		for(int j=1;j<=i;j++){
			dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+f[i][j];
		}
	}
	cout<<dp[1][1]<<endl;
	return 0;
}

显然使用递归也可以实现上面的例子(即从dp[1][1]开始递归,直至到达边界时返回结果)。两者的区别在于:使用递推写法的计算方式是自底向上,即从边界开始,不断向上解决问题,直到解决了目标问题;而使用递归写法的计算方式是自顶向下,即从目标问题开始,将它分解成子问题的组合,直到分解至边界为止。

通过上面的例子再引申出一个概念:如果一个问题的最优解可以由其子问题的最优解有效地构造出来,那么称这个问题拥有最优子结构。最优子结构保证了动态规划中原问题的最优解可以由子问题的最优解推导出来。因此,一个问题必须拥有最优子结构才能够使用动态规划求解。

下面指出两个概念的区别:

(1)分治与动态规划。分治和动态规划都是将问题分解为子问题,然后合并子问题的解得到原问题的解。但是不同的是,分治法分解出的子问题是不重叠的,因此分治法解决的问题不拥有重叠子问题,而动态规划解决的问题拥有重叠子问题。另外,分治法解决的问题不一定是最优化问题,而动态规划解决的问题一定是最优化问题。

(2)贪心与动态规划。贪心和动态规划都要求原问题必须拥有最优子结构。二者的区别在于,贪心法采用的计算方式类似于上面介绍的”自顶向下“,但是并不等待子问题求解完毕后再选择使用哪一个,而是通过一种策略直接选择一个子问题去求解,没被选择的子问题就不去求解了,直接抛弃。也就是说,它总是只在上一步选择的基础上继续选择,因此整个过程以一种单链的流水方式进行,显然这种所谓“最优选择”的正确性需要用归纳法证明。而动态规划不管是自底向上还是自顶向下的计算方式,都是从边界开始向上得到目标问题的解。也就是说,它总是会考虑所有子问题,并选择继承能得到最优结果的那个,对暂时没被继承的子问题,由于重叠子问题的存在,后期可能会再次考虑它们,因此还有机会成为全局最优的一部分,不需要放弃。所以贪心是一种壮士断腕的决策,只要进行了选择,就不后悔;动态规划则要看哪个选择笑到了最后,暂时的领先说明了什么。

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

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

相关文章

论文阅读笔记:Towards Higher Ranks via Adversarial Weight Pruning

论文阅读笔记&#xff1a;Towards Higher Ranks via Adversarial Weight Pruning 1 背景2 创新点3 方法4 模块4.1 问题表述4.2 分析高稀疏度下的权重剪枝4.3 通过SVD进行低秩逼近4.4 保持秩的对抗优化4.5 渐进式剪枝框架 5 效果5.1 和SOTA方法对比5.2 消融实验5.3 开销分析 6 结…

秋招突击——6/14——复习{(树形DP)树的最长路径,(单调队列优化DP)——最大子序和}——新作{四数之和}

文章目录 引言复习树形DP——树的最长路径实现代码答疑 单调队列优化DP——最大子序和个人实现思路参考思路分析实现代码 无重复最长字串思路分析实现代码 新作四数之和实现思路需要注意的问题 参考代码分析思路实现代码 总结 引言 今天好好看看树的最长的路径&#xff0c;自己…

《Deep learning practice》learning notes

学习笔记&#xff1a; 【公开课】旷视x北大《深度学习实践》&#xff08;28课时全&#xff09; R Talk | 旷视科技目标检测概述&#xff1a;Beyond RetinaNet and Mask R-CNN 文章目录 Lecture 1: Introduction to Computer Vision and Deep Learning&#xff08;孙剑&#x…

apt-get update和apt-get upgrade的区别

apt-get update apt-get update 命令用于更新本地软件包列表。具体来说&#xff0c;做了以下事情&#xff1a; ①从 /etc/apt/sources.list 文件和 /etc/apt/sources.list.d/ 目录下的所有文件中读取软件源配置。 ②连接到这些软件源&#xff0c;并下载最新的软件包列表。 ③将…

1832javaERP管理系统之车间计划管理Myeclipse开发mysql数据库servlet结构java编程计算机网页项目

一、源码特点 java erp管理系统之车间计划管理是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助采用了serlvet设计&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统采用web模式&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Mye…

欧洲杯赛况@20240615

点击标题下「蓝色微信名」可快速关注 欧洲杯首战&#xff0c;德国5:1狂胜苏格兰&#xff0c;大比分、红点套餐、超新星登场进球&#xff0c;好像这些能想到的元素都发挥了作用&#xff0c;作为东道主&#xff0c;聚集了天时地利人和&#xff0c;可以说是完美&#xff0c;这就是…

【python】Sklearn—Cluster

参考学习来自 10种聚类算法的完整python操作示例 文章目录 聚类数据集亲和力传播——AffinityPropagation聚合聚类——AgglomerationClusteringBIRCH——Birch&#xff08;✔&#xff09;DBSCAN——DBSCANK均值——KMeansMini-Batch K-均值——MiniBatchKMeans均值漂移聚类——…

21. 第21章 算法分析

21. 算法分析 这个附录选自OReilly Media出版的Alen B.Downey的Think Complexity(2012)一书. 当你读完本书之后, 可能会像继续读读那本书.算法分析是计算机科学的一个分支, 研究算法的性能, 尤其是他们的运行时间和空间需求. 参见http://en.wikipedia.org/wiki/Analysis_of_al…

Mac M3 Pro安装Hadoop-3.3.6

1、下载Hadoop安装包 可以到官方网站下载&#xff0c;也可以使用网盘下载 官网下载地址&#xff1a;Hadoop官网下载地址 网盘地址&#xff1a;https://pan.baidu.com/s/1p4BXq2mvby2B76lmpiEjnA?pwdr62r提取码: r62r 2、解压并添加环境变量 # 将安装包移动到指定目录 mv …

回归预测 | Matlab实现GWO-ESN基于灰狼算法优化回声状态网络的多输入单输出回归预测

回归预测 | Matlab实现GWO-ESN基于灰狼算法优化回声状态网络的多输入单输出回归预测 目录 回归预测 | Matlab实现GWO-ESN基于灰狼算法优化回声状态网络的多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现GWO-ESN基于灰狼算法优化回声状态…

学习前端第一关

作者&#xff1a;周棋洛 文章目录 前言超文本标记语言&#xff1f;编辑器推荐手刃一个网页&#xff0c;Hello World&#xff01;网页骨架讲解标签学习HTML标题HTML段落HTML换行HTML居中HTML水平线HTML格式化HTML注释HTML链接 前言 网站并不是多么复杂的技术&#xff0c;今天这…

leetcode LRU 缓存

leetcode: LRU 缓存 LRU 全称为 Least Recently Used&#xff0c;最近最少使用&#xff0c;常常用于缓存机制&#xff0c;比如 cpu 的 cache 缓存&#xff0c;使用了 LRU 算法。LRU 用于缓存机制时&#xff0c;关键的是当缓存满的时候有新数据需要加载到缓存的&#xff0c;这个…

HTML5的未来:掌握最新技术,打造炫酷网页体验

引言 随着互联网技术的飞速发展&#xff0c;HTML5已经成为构建现代网页和应用的核心技术之一。HTML5不仅提供了丰富的语义化标签&#xff0c;还引入了多项前沿技术&#xff0c;使得网页体验更加丰富多彩。本文将探讨HTML5的最新技术&#xff0c;并结合行业实践&#xff0c;提供…

智能体(Agent)实战——从gpts到auto gen

一.GPTs 智能体以大模型作为大脑&#xff0c;同时配备技能&#xff0c;使其能够完成具体的任务。同时&#xff0c;为了应用于垂直领域&#xff0c;我们需要为大模型定义一个角色&#xff0c;并构建知识库。最后&#xff0c;定义完整的流程&#xff0c;使其完成整个任务。以组会…

GenICam标准(五)

系列文章目录 GenICam标准&#xff08;一&#xff09; GenICam标准&#xff08;二&#xff09; GenICam标准&#xff08;三&#xff09; GenICam标准&#xff08;四&#xff09; GenICam标准&#xff08;五&#xff09; GenICam标准&#xff08;六&#xff09; 文章目录 系列文…

什么是浏览器指纹

在数字互联网时代&#xff0c;我们的在线活动几乎都会留下痕迹。其中&#xff0c;浏览器指纹就像我们的数字身份证&#xff0c;让网站能够识别和追踪用户。本文将详细介绍浏览器指纹是什么&#xff0c;它如何工作。 一、什么是浏览器指纹 浏览器指纹&#xff08;Browser Fing…

【odoo】odoo.conf文件配置

概要 odoo.conf 文件是 Odoo 服务器的配置文件&#xff0c;它用于定义和管理 Odoo 运行时的各种参数。这个文件包含了许多配置选项&#xff0c;可以帮助管理员根据特定的需求和环境来调整 Odoo 服务器的行为。 主要功能 数据库连接设置&#xff1a;定义 Odoo 连接到 PostgreSQL…

vue项目问题汇总

1.el-select&#xff1a; 下拉框显示到了top:-2183px , 添加属性 :popper-append-to-body"false" 2. el-upload: 选过的文件在使用过后记得清空&#xff0c;因为如果有limit1的时候&#xff0c;没有清空会导致不触发onchange 使用自定义上传方法http-request的时…

C++ 47 之 函数调用运算符重载

#include <iostream> #include <string> using namespace std;class MyPrint{ public:// 重载小括号() 重载谁operator后就紧跟谁的符号void operator()(string txt){cout << txt << endl;} };class MyAdd{ public:int operator()(int a, int b){retur…

Android 断点续传实现原理

下载原理 在介绍断点续传之前&#xff0c;我们先说说下载的原理。代码示例用 OkHttp 作为示例。 下载核心思路是把 responseBody 写入文件&#xff0c;核心代码如下&#xff1a; 但是这种做法有个明显的问题&#xff0c;假如手机在下载文件的时候下载了80%&#xff0c;某些原…