动态规划(DP)(算法笔记)

article/2025/9/29 9:04:37

本文内容基于《算法笔记》和官方配套练题网站“晴问算法”,是我作为小白的学习记录,如有错误还请体谅,可以留下您的宝贵意见,不胜感激。

文章目录

  • 前言
  • 一、动态规划概述
  • 二、算法设计
  • 1.上楼||
  • 2.最大连续子序列和
      • 动态规划
      • 分治
  • 3.最大连续子序列和的最优方案
    • 三、备注


前言

动态规划(Dynamic Programming,DP)是一种用来解决一类最优化问题的算法思想。简单来说,动态规划将一个复杂的问题分解成若干个子问题,通过综合子问题的最优解来得到原问题的最优解。需要注意的是,动态规划会将每个求解过的子向题的解记录下来,这样当下一次碰到同样的子问题时,就可以直接使用之前记录的结果,而不是重复计算。注意:虽然动态规划采用这种方式来提高计算效率,但不能说这种做法就是动态规划的核心。一般可以使用递归或者递推的写法来实现动态规划,其中递归写法在此处又称作记忆化搜索,递推写法是自底向上,递归写法是自顶向下(和面向过程和面向对象相似但不完全一样)。

一、动态规划概述

动态规划粗俗点可以说是对分治算法的优化,一切可以用动态规划解决的问题都可以用分治解决,其核心是状态转移方程。动态规划是解决最优解的算法,一个问题必须拥有重叠子问题和最优子结构,才能用动态规划去解决。重叠子问题很好理解,在递归中又被称为记忆化搜索;最优子结构指原问题的最优解可以由其子问题的最优解构造出来,并不一定是满足分治法那样到达递归边界即为解,而是需要将子问题的最优解记录下来,通过某种关系用子问题的最优解“构造”出原问题的最优解,至于怎么构造,就取决于个人理解了。
所以,动态规划的解题步骤如下:
1.通过原问题分析最优子结构,即将原问题分解为子问题,分析子问题中存在的重叠子问题;
2.设置状态dp记录子问题的最优解,设计状态转移方程描述原问题与子问题之间的递推关系(当然可以用分治法来分析原问题并采用递归)。
3.求解dp,并通过dp来构造原问题的最优解。

二、算法设计

1.上楼||

在这里插入图片描述
首先先把这道题的描述转化为最优解问题,即求最大上楼策略。
1.问题分解:找到题中的子问题,这很容易,就是走一级台阶或者两级台阶,所以可以画出问题分解图如下:
在这里插入图片描述
当然也可以从n向下画;
2.设计状态和状态转移方程:dp[i]记录走到某个台阶时继续向下走的最大方案数,所以可以设计状态转移方程为:dp[i] = dp[i + 1] + dp[i + 2];
3.从底层开始循环,通过状态转移方程扩散到整个数组,dp[0]即为求解。
注:递归实现更简单,这里就不赘述了。
完整代码如下

#include<cstdio>
const int MAXN = 10000;
int dp[MAXN] = {};
int main(){int n;scanf("%d", &n);dp[n] = 0;dp[n - 1] = 1;dp[n - 2] = 2;for(int i = n - 3; i >= 0; i--)dp[i] = (dp[i + 1] + dp[i + 2]) % 10007;printf("%d", dp[0]);
}

2.最大连续子序列和

在这里插入图片描述
这道题有两种解法,分治法和动态规划,这里先分析动态规划法。

动态规划

1.问题分解:将原问题这样分解:求a1····an-1序列的连续子序列最大值,并判断A[i]的状态是加入序列或者以A[i]为子序列首元素;求a1···an-2······
2.设计状态和状态转移方程:dp[i]为以A[i]为结尾连续序列的最大和,所以可以设计状态转移方程为:dp[i] = max(A[i] , dp[i - 1] + A[i]);通过这个状态转移方程可以认识到并不需要枚举左端点,因为每一个子问题的最优解都可以借由前一个子问题的最优解推导出来;
3.从底层开始循环,通过状态转移方程扩散到整个数组,dp[i]中的最大值就是原问题的最优解,注意不是dp[n]。
完整代码如下:

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 10000;
int A[MAXN] = {};
int dp[MAXN] = {};int main(){int n;scanf("%d", &n);for(int i = 0; i <= n - 1; i++) scanf("%d", A + i);dp[0] = A[0];int MAX = -10e5;for(int i = 1; i <= n - 1; i++) {dp[i] = max(A[i] , dp[i - 1] + A[i]);if(dp[i] > MAX) MAX = dp[i];}printf("%d", MAX);
}

分治

1.问题分解:
如果将所给的序列A[1…n]分为长度相等的两段A[1…n/2]和A[n/2+1…n],分别求出这两段的最大字段和,则A[1…n]的最大子段和有3种情形:
(1)A[1…n]的最大子段和与A[1…n/2]的最大子段和相同;
(2)A[1…n]的最大子段和与A[n/2+1…n]的最大子段和相同;
(3)A[1…n]的最大子段和是在A[1…n/2]和A[n/2+1…n]上的两个片段,即在A[1…n]的中间位置;
2.解决:
(1)和(2)采用递归解决,对(3)可以从n/2和n/2+1为起点,分别在两个片段中求出最大子序列和,然后求和即为A[1…n]的最大子段和;
3.合并:
比较在分解阶段的3种情况下的最大子段和,取三者中的较大者为原问题的解。
完整代码就先不放了(没时间写了···),对于这本书,最重要的是第一种方法,因为之后的例题都是采用这种思路的。

3.最大连续子序列和的最优方案

在这里插入图片描述
这道题在上一道题的基础上,需要记录到达每一个以A[i]为结尾的最大连续子序列的首元素和尾元素,所以可以开一个结构体存储首尾元素位置,随着动态规划的求解不断记录首尾元素,最后将第一个最大子序列和对应的以A[i]为结尾的首尾元素。其中,结构体的记录有三种情况:
if(A[i] > dp[i - 1] + A[i]) Node[i] = {i , i};
if(A[i] < dp[i - 1] + A[i]) Node[i] = {Node[i - 1].i , i};
if(A[i] == dp[i - 1] + A[i]) Node[i] = {Node[i - 1].i , i};
前两个分别代表以A[i]为新的子序列首尾元素和将A[i]加入上一个序列的情况。但是在首尾元素的选择上还存在第三种情况,即A[i] == dp[i - 1] + A[i],这个情况在计算最大和时并不需要考虑在内,但现在是要求具体的首尾元素位置,并且在存在多解的情况下总是选择首元素下标最小的解(在判断最大值时就已经是尾元素下标最小的情况了),那么当碰到A[i] == dp[i - 1] + A[i]时,意味着A[i]和dp[i - 1] + A[i]都可以满足情况,这时为了满足首元素位置最小的情况,就将A[i]加入前一序列,而不是以A[i]为新序列的首元素。
完整代码如下:

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 10000;
int A[MAXN] = {};
int dp[MAXN] = {};
struct node{int i , j;
}Node[MAXN]; //记录当前序列的首位坐标 int main(){int n;scanf("%d", &n);for(int i = 0; i <= n - 1; i++) scanf("%d", A + i);dp[0] = A[0];Node[0] = {0 , 0};int MAX = -10e5;int MAXI = 0;for(int i = 1; i <= n - 1; i++) {dp[i] = max(A[i] , dp[i - 1] + A[i]);if(dp[i] > MAX) {    //记录的就是最小的尾元素下标MAX = dp[i];MAXI = i;   //记录结尾 }if(A[i] > dp[i - 1] + A[i]) Node[i] = {i , i};  //以A[i]为首尾if(A[i] < dp[i - 1] + A[i]) Node[i] = {Node[i - 1].i , i};    //将A[i]加入上一个序列 if(A[i] == dp[i - 1] + A[i]) Node[i] = {Node[i - 1].i , i};  //总是将A[i]加入上一个序列 }printf("%d %d %d", MAX , Node[MAXI].i + 1 , Node[MAXI].j + 1);
}

三、备注

1.注意体会重叠子问题和最优子结构在递归和递推中的体现;
2.原问题的最优解是由子问题的最优解构建出来的,这一点还是区别于分治法的将问题缩小到最小子问题解决的思路;
3.分治与动态规划的区别(摘自算法笔记):分治和动态规划都是将问题分解为子问题,然后合并子问题的解得原问题的解。但是不同的是,分治法分解出的子问题是不重叠的,因此分治法解决的问题不拥有重叠于间题,而动态规划解决的问题拥有重叠子问题。例如,归并样序和快速排序是分别处理左序列和右序列,然后将左右序列的结果合并,过程中不出现重叠子问题,因此它们使用的都是分治法。另外,分治决的问题不一定是最优化问愿,而动态规划解决的问题一定是最优化问题。
4.贪心与动态规划的区别(摘自算法笔记):贪心和动态规划都要求原问题必须拥有最优子结构。二者的区别在于,贪心法采用的计算方式类似于上面介绍的“自顶向下”,但是并不等待子问题求解完毕后再选择使用哪一个,而是通过一种策略直接选择一个子问题去求解,没被选择的子问题就不去求解了,直接抛弃。也就是说,它总是只在上一步选择的基础上继续选择,因此整个过程以一种单链的流水方式进行,显然这种所谓“最优选择”的正确性需要用归纳法证明。例如对数塔问题而言,贪心法从最上层开始,每次选择左下和右下两个数字中较大的一个,一直到最底层得到最后结果,显然这不一定可以得到最优解。而动态规划不管是采用自底向上还是自项向下的计算方式,都是从边界开始向上得到目标问题的解。也就是说,它总是会考虑所有子问题,并选择继承能得到最优结果的那个,对暂时没被继承的子问题,由于重叠子问题的存在,后期可能会再次考虑它们,因此还有机会成为全局最优的一部分,不需要放弃。所以贪心是一种壮士断腕的决策,只要进行了选择,就不后悔:动态规划则要看哪个选择笑到了最后,暂时的领先说明不了什么。


http://chatgpt.dhexx.cn/article/IsJArEbh.shtml

相关文章

免费馅饼 (DP动态规划问题详细解析)

免费馅饼 HDU - 1176 都说天上不会掉馅饼&#xff0c;但有一天gameboy正走在回家的小径上&#xff0c;忽然天上掉下大把大把的馅饼。说来gameboy的人品实在是太好了&#xff0c;这馅饼别处都不掉&#xff0c;就掉落在他身旁的10米范围内。馅饼如果掉在了地上当然就不能吃了&am…

动态规划的理解(DP)

动态规划&#xff08;DP&#xff09; 文章目录 动态规划&#xff08;DP&#xff09;1.动态规划概念2.最短路径问题3.动态规划和分治区别4.为什么动态规划往往从后往前&#xff1f; 1.动态规划概念 在现实生活中&#xff0c;有一类活动的过程&#xff0c;由于它的特殊性&#xf…

动态规划(DP)小结

目录 动规解题的一般思路 能用动规解决的问题的特点 动归的常用形式 例题 例题一&#xff1a;最长公共子序列 例题二&#xff1a;最大子段和 例题三&#xff1a;最长上升子序列(最长单调递增) 例题四&#xff1a;数字三角形 例题五 0-1背包问题 应用题 应用一&#x…

动态规划 DP专题

跟着ygg的dp题单刷的dp 1.代码源每日一题 Div1 连续子序列 分析&#xff1a; dp数组开成map&#xff0c;则状态转移式dp[i] max(dp[i - 1] 1, dp[i]) AC代码&#xff1a; #include <bits/stdc.h>using namespace std; typedef long long ll; #define int ll #define …

动态规划之线性DP

&#x1f40f;&#x1f40f;&#x1f40f; &#x1f40f;动态规划之线性DP&#x1f40f;&#x1f40f;写在前面&#x1f40f;&#x1f40f;数字三角形&#x1f40f;&#x1f40f;最长上升子序列&#x1f40f;&#x1f40f;最长上升子序列 II&#x1f40f;&#x1f40f;最长公共…

DP(动态规划)入门基础详解

DP总结&#xff08;写得这么辛苦点个赞呗&#xff01;&#xff09; DP基本概要&#xff1a; 动态规划算法把原问题视作若干个重叠子问题的逐层递进,每一个子问题的求解过程都构成一个“阶段”。在完成前一个阶段的计算后,动态规划才会执行下一阶段的计算。 无后效性 &#xf…

非常好的动态规划(DP)总结

转自&#xff1a; http://cppblog.com/menjitianya/archive/2015/10/23/212084.html 目录 一、动态规划初探 1、递推 2、记忆化搜索 3、状态和状态转移 4、最优化原理和最优子结构 5、决策和无后效性 二、动态规划的经典模型 1、线性模型 2、区间模型 3、背包模型 4、状态压…

动态规划 DP (一)

1.动态规划&#xff08;Dynamic Programming&#xff0c;简称DP&#xff09; 维基百科的定义说的很清楚&#xff1a; 动态规划不能解决所有的问题&#xff0c; 只能应用于有最优子结构的问题。例如背包问题、最长公共子序列问题、最短路径问题等。 最优子结构&#xff1a;局部…

动态规划(DP)通俗讲解

参考 徐凯强 Andy 动态规划中递推式的求解方法不是动态规划的本质。 我曾经作为省队成员参加过NOI&#xff0c;保送之后也给学校参加NOIP的同学多次讲过动态规划&#xff0c;我试着讲一下我理解的动态规划&#xff0c;争取深入浅出。希望你看了我的答案&#xff0c;能够喜欢上动…

【算法之动态规划(一)】动态规划(DP)详解

一、基本概念 动态规划(dynamic programming)是 运筹学 的一个分支&#xff0c;是求解决策过程(decision process)最优化的数学方法。20世纪50年代初 美国 数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时&#xff0c;提出了著名的最优…

动态规划(dp)的总结

动态规划(dp)的总结 动态规划只要找到子问题&#xff0c;写起来就很简单&#xff0c;通常最多就二维dp数组即可解决问题&#xff0c;顶多再来个双dp&#xff0c;再加点逆向思维……下面列出我见过的子问题&#xff0c;别栽在dp上了&#xff0c;求求了。 能用dp做&#xff0c;…

数据结构与算法——动态规划(DP)

文章目录 1. 应用场景2. DP状态2.1 最优子结构2.2 无后效性2.3 解题思路 3. 问题类别3.1 线性DP3.1.1 经典问题3.1.1.1 [LeetCode 300. 最长上升子序列](https://leetcode-cn.com/problems/longest-increasing-subsequence/)3.1.1.2 [LeetCode 1143. 最长公共子序列](https://l…

关于动态规划(dp)

**动态规划(DP) 一.基本概念 动态规划&#xff08;英语&#xff1a;Dynamic programming&#xff0c;简称DP&#xff09;是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的&#xff0c;通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。 它针对满足…

动态规划(DP)的原理、实现及应用

文章目录 1. 由一个例子说开&#xff1a; 斐波那契&#xff08;fibonacci&#xff09;数列 性能测试原因分析2. 记忆化搜索3. 动态规划&#xff08;Dynamic Programming&#xff0c;DP&#xff09; 最优子结构总结一下这几个解法&#xff1a;几个例题 LeetCode 70 Climbing St…

Hi3519AV100与Hi3559AV100在芯片规格 上主要差异

表1-1简要对比了Hi3519AV100与Hi3559AV100在规格方面的差异&#xff0c;Hi3519AV100的具体规格请参见《Hi3519AV100 ultra-HD Mobile Camera SoC 用户指南》。

海思平台(hi3559av100)异构多系统的使用Linux(2*A53+2*A73)+liteos(A53)+liteos(M7)

在文档《SDK安装及升级使用说明》中有对linuxliteos异构多系统的烧写有介绍。这里对其中的一些注意的地方记录以下&#xff0c;以备查验。 由于我的目标是要搭建一个ISP调试环境&#xff0c;就是使用海思的ittp_stream工具能够连接上开发板&#xff0c;并能够实时查看摄像头的…

M302H-ZN-Hi3798MV300/MV300H-当贝纯净桌面-卡刷固件包

M302H-ZN-Hi3798MV300&#xff0f;MV300H-当贝纯净桌面-卡刷固件包-内有教程 特点&#xff1a; 1、适用于对应型号的电视盒子刷机&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、修改dns&#xff0c;三网通用&#xff1b; 4、大量精简内置的没用…

华为海思 hikey970 详细介绍

前几天申请到了华为的开发板&#xff1a;hikey970 用来做项目的。 板子是这样的: 下面是在网中收集到的信息总结&#xff1a; 基于麒麟970的AI智慧算力&#xff0c;HiKey 970除了支持CPU和GPU的AI运算外&#xff0c;还支持基于NPU的神经网络计算硬件加速。 公开资料显示&am…

海思Hi3519AV100 emmc flash方式 linux系统移植 hitool工具烧写

因为我这里的海思文档只有SPI NOR Flash方式的详细烧写步骤&#xff0c;没有emmc方式的&#xff0c;本文提供一个自己成功的案例仅供参考和记录 1. 准备SDK、安装交叉编译工具、编译osdrv 1.1 解压SDK包 将Hi3519AV100_SDK_Vx.x.x.x.tgz文件放入ubuntu系统下&#xff08;wind…

海思3559:MMZ内存、OS内存配置

前言 海思3559的DDR最大支持到8GB hi3559av100芯片的内存地址范围 (1)通过查阅数据手册可知《Hi3559AV100 专业型 Smart IP Camera SoC 用户指南》&#xff0c;芯片的内存地址范围是0x4000_0000-0x23FFF_FFFF&#xff0c;最大能支持8G内存&#xff1b;   (2)海思芯片把内存分…