深入理解动态规划(dp)--(提前要对dfs有了解)
前言:对于动态规划:该算法思维是在dfs基础上演化发展来的,所以我不想讲的是看到一个题怎样直接用动态规划来解决,而是说先用dfs搜索,一步步优化,这个过程叫做动态规划。(该文章教你怎样一步步的解决这类题)
目录
一、动态规划入门
二、跳台阶问题---来自AcWing
1.用暴力搜索dfs来解
2.记忆化搜索实现
3.递推实现
二、大盗阿福---来自AcWing
1.用dfs暴力搜索
2.记忆化搜索
3.递推实现
四、数字三角形---来自洛谷
1.用暴力搜索dfs
2.用记忆化搜索
3.递推dp
一、动态规划入门
动态规划就是:给定一个问题,我们将它拆解为一个个子问题,直到子问题可以直接解决,然后把子问题的答案保存起来,以减少重复计算,再根据子问题答案反推,得出原问题的一种方法
动态规划入门思路:dfs暴力--->记忆化搜索--->递推DP
下面正式开始讲解,还是在题中带大家慢慢理解动态规划的思维
二、跳台阶问题---来自AcWing
一个楼梯共有n级台阶,每次可以走一级或者两级,问从第0级台阶走到第n级台阶一共有多少种方案。
输入格式:
共一行,包含一个整数n
输出格式:
共一行,包含一个整数,表示方案数
数据范围:
1<=n<=15
输入样例:
5
输出样例:
8
1.用暴力搜索dfs来解
- 这个题大部分同学都应该见过,最初我们用递归来解决这道题,其实本质上也是dfs暴力搜索
#include<iostream>
#include<algorithm>
using namespace std;
int n;
int fib(int x)
{if (x == 1)return 1;else if (x == 2)return 2;else return fib(x - 1) + fib(x - 2);
}
int main(void)
{cin >> n;int res = fib(n);cout << res << endl;return 0;
}

这时我们会发现,当n=41时,时间就快到了1s,所以要想办法去优化代码
2.记忆化搜索实现
这里我拿n=5为例,来画一下搜索树,然后分析一下怎么优化

如果是用一个数组来存储一下的话,直接就省去了这棵大树的右分支,因为左分支中的3已经搜索过了,当以后遇到别的题或者n更大时这棵树的左右分支也会很大,所以省去的搜索也就更多。
#include<iostream>
#include<algorithm>
using namespace std;
int arr[100];
int n;
int fib(int x)
{if(arr[x])return arr[x];int sum=0;if (x == 1) sum=1;else if (x == 2) sum=2;else sum=fib(x-1)+fib(x-2);arr[x]=sum;return sum;
}
int main(void)
{scanf("%d",&n);int res = fib(n);printf("%d\n",res);return 0;
}

直接将900多毫秒优化到了2毫秒。
3.递推实现
递归的过程:“归”的过程才是产生答案的过程
“递”的过程是分解子问题的过程(把大问题分解为子问题)
“递”:自顶向下
“归”:自底向上
而我们自底向上一步步推出答案的过程-----就是递推
好,接下来就用递推的方式进行编程:
#include<iostream>
#include<algorithm>
using namespace std;
int F[100];
int n;
int main(void)
{scanf("%d",&n);F[1]=1,F[2]=2;for(int i=3;i<=n;i++){F[i]=F[i-2]+F[i-1];//这个递推公式也就是dfs的状态转移公式}printf("%d\n",F[n]);return 0;
}
总结:
跳台阶这道题:我们就是这样做的:
最暴力的dfs--->记忆化搜索--->递推(dp)
记忆化搜索=暴力bfs+记录答案
递推的公式=dfs向下递归的公式
递推数组的初始值=递归的边界
二、大盗阿福---来自AcWing
阿福是一名经验丰富的大盗。趁着月黑风高,阿福打算今晚洗劫一条街上的店铺。这条街上一共有 N 家店铺,每家店中都有一些现金。阿福事先调查得知,只有当他同时洗劫了两家相邻的店铺时,街上的报警系统才会启动,然后警察就会蜂拥而至。
作为一向谨慎作案的大盗,阿福不愿意冒着被警察追捕的风险行窃。
他想知道,在不惊动警察的情况下,他今晚最多可以得到多少现金?
输入格式:
输入的第一行是一个整数 T,表示一共有 T 组数据。接下来的每组数据,第一行是一个整数 N ,表示一共有 N 家店铺,第二行是 N 个被空格分开的正整数,表示每一家店铺中的现金数量每家店铺中的现金数量均不超过1000。
输出格式:对于每组数据,输出一行
该行包含一个整数,表示阿福在不惊动警察的情况下可以得到的现金
范围:
1<=T<=50
1<=N<=1e5
输入样例:
2
3
1 8 2
4
10 7 6 14
输出样例:
8
24
1.用dfs暴力搜索
先画搜索树,这道题是选和不选问题

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int arr[N];
int n, t;
int res = 0;
int dfs(int x)//x表示当前正在考虑哪家店
{if (x > n)return 0;else return max(dfs(x + 1), dfs(x + 2) + arr[x]);
}
int main(void)
{cin >> t;while (t--){cin >> n;for (int i = 1; i <= n; i++)scanf_s("%d", &arr[i]);int res = dfs(1);}return 0;
}
放到官网提交一下答案发现,时间超时,因为dfs的时间复杂度是2的n次方,超时是理所当然的事,还是要想办法优化
2.记忆化搜索
要想实现记忆化搜索的话,那么dfs的参数就需要尽可能的少,不应该把没有影响到边界的参数放进来
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int arr[N];
int mem[N];
int n, t;
int res = 0;
int dfs(int x)//x表示当前正在考虑哪家店
{if (mem[x])return mem[x];int sum = 0;if (x > n) sum = 0;else sum = max(dfs(x + 1), dfs(x + 2) + arr[x]);mem[x] = sum;return sum;
}
int main(void)
{cin >> t;while (t--){cin >> n;for (int i = 1; i <= n; i++)scanf_s("%d", &arr[i]);memset(mem, 0, sizeof mem);int res = dfs(1);}return 0;
}
跟跳台阶一样的套路,创建一个数组,存放数据。
3.递推实现
前面也说过了,递推的过程就是递归的“归”,由搜索树的最底层开始向上推,并且递推的公式就是向下递归的公式.
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int arr[N];
//int mem[N];
int f[N];
int n, t;
int res = 0;
#if 0
int dfs(int x)//x表示当前正在考虑哪家店
{if (mem[x])return mem[x];int sum = 0;if (x > n) sum = 0;else sum = max(dfs(x + 1), dfs(x + 2) + arr[x]);mem[x] = sum;return sum;
}
#endif
int main(void)
{cin >> t;while (t--){cin >> n;for (int i = 1; i <= n; i++)scanf_s("%d", &arr[i]);//memset(mem, 0, sizeof mem);memset(f, 0, sizeof f);for (int i = n; i >= 0; i--){f[i] = max(f[i + 1], f[i + 2] + arr[i]);}//int res = dfs(1);}return 0;
}
四、数字三角形---来自洛谷

还是一样的套路,三种方法解决问题(我希望大家先自己去尝试用这三种方法动手打一下代码,哪里有不明白的直接看代码再自己理解一下,编程还是自己去上手才能看出来明白还是不明白)
1.用暴力搜索dfs
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1005;
int arr[N][N];
int n;
int dfs(int x, int y)
{if (x > n || y > n)return 0;else return max(dfs(x + 1, y) + arr[x][y], dfs(x + 1, y + 1) + arr[x][y]);
}
int main(void)
{cin >> n;for (int i = 1; i <= n; i++){for (int j = 1; j <= i; j++){cin >> arr[i][j];}}int res = dfs(1, 1);cout << res << endl;return 0;
}
2.用记忆化搜索
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1005;
int arr[N][N];
int mem[N][N];
int n;
int dfs(int x, int y)
{if(mem[x][y])return mem[x][y];int sum=0;if (x > n || y > n) sum = 0;else sum = max(dfs(x + 1, y) + arr[x][y], dfs(x + 1, y + 1) + arr[x][y]);mem[x][y]=sum;return sum;
}
int main(void)
{cin >> n;for (int i = 1; i <= n; i++){for (int j = 1; j <= i; j++){cin >> arr[i][j];}}memset(mem,0,sizeof mem);int res = dfs(1, 1);cout << res << endl;return 0;
}
3.递推dp
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1005;
int arr[N][N];
int f[N][N];
int n;
int main(void)
{cin >> n;for (int i = 1; i <= n; i++){for (int j = 1; j <= i; j++){cin >> arr[i][j];}}for (int i = n; i >= 1; i--){for (int j = 1; j <= i; j++){f[i][j] = max(f[i + 1][j] + arr[i][j], f[i + 1] [j + 1] + arr[i][j]);}}cout << f[1][1] << endl;return 0;
}
最后:希望读完这篇文章的你,对动态规划有了更深入的了解!
相关文章:
深入理解动态规划(dp)--(提前要对dfs有了解)
前言:对于动态规划:该算法思维是在dfs基础上演化发展来的,所以我不想讲的是看到一个题怎样直接用动态规划来解决,而是说先用dfs搜索,一步步优化,这个过程叫做动态规划。(该文章教你怎样一步步的…...
(1)STM32 USB设备开发-基础知识
开篇感谢: 【经验分享】STM32 USB相关知识扫盲 - STM32团队 ST意法半导体中文论坛 单片机学习记录_桃成蹊2.0的博客-CSDN博客 USB_不吃鱼的猫丿的博客-CSDN博客 1、USB鼠标_哔哩哔哩_bilibili usb_冰糖葫的博客-CSDN博客 USB_lqonlylove的博客-CSDN博客 USB …...
基于STM32单片机设计的宠物喂食监控系统
1. 项目开发背景 随着宠物数量的增加,尤其是人们对宠物的养护需求日益增多,传统的人工喂养和管理方式难以满足现代养宠生活的需求。人们越来越希望通过智能化手段提高宠物养护的质量和效率,特别是对于宠物喂食、饮水、温湿度控制等方面的智能…...
MATLAB绘图:随机彩色圆点图
这段代码在MATLAB中生成并绘制了500个随机位置和颜色的散点图。通过随机生成的x和y坐标以及颜色,用户可以直观地观察到随机点的分布。这种可视化方式在数据分析、统计学和随机过程的演示中具有广泛的应用。 文章目录 运行结果代码代码讲解 运行结果 代码 clc; clea…...
重定向与缓冲区
4种重定向 我们有如下的代码: #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h>#define FILE_NAME "log.txt"int main() {close(1)…...
【Elasticsearch】index:false
在 Elasticsearch 中,index 参数用于控制是否对某个字段建立索引。当设置 index: false 时,意味着该字段不会被编入倒排索引中,因此不能直接用于搜索查询。然而,这并不意味着该字段完全不可访问或没有其他用途。以下是关于 index:…...
Golang Gin系列-8:单元测试与调试技术
在本章中,我们将探讨如何为Gin应用程序编写单元测试,使用有效的调试技术,以及优化性能。这包括设置测试环境、为处理程序和中间件编写测试、使用日志记录、使用调试工具以及分析应用程序以提高性能。 为Gin应用程序编写单元测试 设置测试环境…...
九、CSS工程化方案
一、PostCSS介绍 二、PostCSS插件的使用 项目安装 - npm install postcss-cli 全局安装 - npm install postcss-cli -g postcss-cli地址:GitHub - postcss/postcss-cli: CLI for postcss postcss地址:GitHub - postcss/postcss: Transforming styles…...
YOLOv11改进,YOLOv11检测头融合DSConv(动态蛇形卷积),并添加小目标检测层(四头检测),适合目标检测、分割等任务
前言 精确分割拓扑管状结构例如血管和道路,对各个领域至关重要,可确保下游任务的准确性和效率。然而,许多因素使任务变得复杂,包括细小脆弱的局部结构和复杂多变的全局形态。在这项工作中,注意到管状结构的特殊特征,并利用这一知识来引导 DSCNet 在三个阶段同时增强感知…...
大数据治理实战指南:数据质量、合规与治理架构
📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 引言 随着企业数字化转型的加速,大数据已成为驱动业务决策的核心资产。然而,数据治理的缺失或不完善&…...
SQL Server 建立每日自动log备份的维护计划
SQLServer数据库可以使用维护计划完成数据库的自动备份,下面以在SQL Server 2012为例说明具体配置方法。 1.启动SQL Server Management Studio,在【对象资源管理器】窗格中选择数据库实例,然后依次选择【管理】→【维护计划】选项࿰…...
three.js+WebGL踩坑经验合集(4.2):为什么不在可视范围内的3D点投影到2D的结果这么不可靠
上一篇,笔者留下了一个问题,three.js内置的THREE.Vector3.project方法算出来的结果对于超出屏幕可见范围的点来说错得相当离谱。 three.jsWebGL踩坑经验合集(4.1):THREE.Line2的射线检测问题(注意本篇说的是Line2,同样也不是阈值…...
window保存好看的桌面壁纸
1、按下【WINR】快捷键调出“运行”窗口,输入以下命令后回车。 %localappdata%\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets 2、依次点击【查看】【显示】,勾选【隐藏的项目】,然后按【CtrlA】全部…...
Protobuf序列化协议使用指南
简介 在本篇博客中,将会介绍protobuf的理论及使用方法。该文章仅做分享使用及自我复习使用,使用的图片来自百度,无法找到作者,如若侵权请联系删除。 目录 简介 概述 1.protobuf是什么? 2.序列化/反序列是什么&…...
83,【7】BUUCTF WEB [MRCTF2020]你传你[特殊字符]呢
进入靶场 图片上这个人和另一道题上的人长得好像 54,【4】BUUCTF WEB GYCTF2020Ezsqli-CSDN博客 让我们上传文件 桌面有啥传啥 /var/www/html/upload/344434f245b7ac3a4fae0a6342d1f94a/123.php.jpg 成功后我就去用蚁剑连了,连不上 看了别的wp知需要…...
低代码系统-产品架构案例介绍、轻流(九)
轻流低代码产品定位为零代码产品,试图通过搭建来降低企业成本,提升业务上线效率。 依旧是从下至上,从左至右的顺序 名词概述运维层底层系统运维层,例如上线、部署等基础服务体系内置的系统能力,发消息、组织和权限是必…...
Linux——网络(udp)
文章目录 目录 文章目录 前言 一、upd函数及接口介绍 1. 创建套接字 - socket 函数 2. 绑定地址和端口 - bind 函数 3. 发送数据 - sendto 函数 4. 接收数据 - recvfrom 函数 5. 关闭套接字 - close 函数 二、代码示例 1.服务端 2.客户端 总结 前言 Linux——网络基础…...
Nxopen 直齿轮参数化设计
NXUG1953 Visualstudio 2019 参考论文: A Method for Determining the AGMA Tooth Form Factor from Equations for the Generated Tooth Root Fillet //FullGear// Mandatory UF Includes #include <uf.h> #include <uf_object_types.h>// Internal I…...
初阶数据结构:链表(二)
目录 一、前言 二、带头双向循环链表 1.带头双向循环链表的结构 (1)什么是带头? (2)什么是双向呢? (3)那什么是循环呢? 2.带头双向循环链表的实现 (1)节点结构 (2…...
Rust:高性能与安全并行的编程语言
引言 在现代编程世界里,开发者面临的最大挑战之一就是如何平衡性能与安全性。在许多情况下,C/C这样的系统级编程语言虽然性能强大,但其内存管理的复杂性导致了各种安全漏洞。为了解决这些问题,Rust 作为一种新的系统级编程语言进入…...
使用openwrt搭建ipsec隧道
背景:最近同事遇到了个ipsec问题,做的ipsec特性,ftp下载ipv6性能只有100kb, 正面定位该问题也蛮久了,项目没有用openwrt, 不过用了开源组件strongswan, 加密算法这些也是内核自带的,想着开源的不太可能有问题ÿ…...
网络安全 | F5-Attack Signatures详解
关注:CodingTechWork 关于攻击签名 攻击签名是用于识别 Web 应用程序及其组件上攻击或攻击类型的规则或模式。安全策略将攻击签名中的模式与请求和响应的内容进行比较,以查找潜在的攻击。有些签名旨在保护特定的操作系统、Web 服务器、数据库、框架或应…...
MATLAB绘图时线段颜色、数据点形状与颜色等设置,介绍
MATLAB在绘图时,设置线段颜色和数据点的形状与颜色是提高图形可读性与美观性的重要手段。本文将详细介绍如何在 MATLAB 中设置这些属性。 文章目录 线段颜色设置单字母颜色表示法RGB 值表示法 数据点的形状与颜色设置设置数据点颜色和形状示例代码 运行结果小结 线段…...
论文速读|Matrix-SSL:Matrix Information Theory for Self-Supervised Learning.ICML24
论文地址:Matrix Information Theory for Self-Supervised Learning 代码地址:https://github.com/yifanzhang-pro/matrix-ssl bib引用: article{zhang2023matrix,title{Matrix Information Theory for Self-Supervised Learning},author{Zh…...
FPGA工程师成长四阶段
朋友,你有入行三年、五年、十年的职业规划吗?你知道你所做的岗位未来该如何成长吗? FPGA行业的发展近几年是蓬勃发展,有越来越多的人才想要或已经踏进了FPGA行业的大门。很多同学在入行FPGA之前,都会抱着满腹对职业发…...
计算机组成原理(2)王道学习笔记
数据的表示和运算 提问:1.数据如何在计算机中表示? 2.运算器如何实现数据的算术、逻辑运算? 十进制计数法 古印度人发明了阿拉伯数字:0,1,2,3,4,5,6&#…...
Spring中的事件和事件监听器是如何工作的?
目录 一、事件(Event) 二、事件发布器(Event Publisher) 三、事件监听器(Event Listener) 四、使用场景 五、总结 以下是关于Spring中的事件和事件监听器的介绍与使用说明,结合了使用场景&…...
3097. 或值至少为 K 的最短子数组 II
3097. 或值至少为 K 的最短子数组 II 题目链接:3097. 或值至少为 K 的最短子数组 II 代码如下: class Solution { public:int minimumSubarrayLength(vector<int>& nums, int k) {int res INT_MAX;for (int i 0;i < nums.size();i) {in…...
简化配置与动态表达式的 Spring EL
1 引言 在现代软件开发中,配置管理和动态逻辑处理是构建灵活、可维护应用程序的关键。Spring 框架以其强大的依赖注入和面向切面编程功能而闻名,而 Spring Expression Language (Spring EL) 则为开发者提供了一种简洁且强大的方式来简化配置并实现动态表达式。 1.1 Spring …...
erase() 【删数函数】的使用
**2025 - 01 - 25 - 第 48 篇 【函数的使用】 作者(Author) 文章目录 earse() - 删除函数一. vector中的 erase1 移除单个元素2 移除一段元素 二. map 中的erase1 通过键移除元素2 通过迭代器移除元素 earse() - 删除函数 一. vector中的 erase vector 是一个动态数组&#x…...
