深入理解动态规划(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搜索,一步步优化,这个过程叫做动态规划。(该文章教你怎样一步步的…...
单片机基础模块学习——数码管(二)
一、数码管模块代码 这部分包括将数码管想要显示的字符转换成对应段码的函数,另外还包括数码管显示函数 值得注意的是对于小数点和不显示部分的处理方式 由于小数点没有单独占一位,所以这里用到了两个变量i,j用于跳过小数点导致的占据其他字符显示在数…...
【大数据】机器学习----------强化学习机器学习阶段尾声
一、强化学习的基本概念 注: 圈图与折线图引用知乎博主斜杠青年 1. 任务与奖赏 任务:强化学习的目标是让智能体(agent)在一个环境(environment)中采取一系列行动(actions)以完成一个…...
flink写parquet解决timestamp时间格式字段问题
背景 Apache Parquet 是一种开源的列式数据文件格式,旨在实现高效的数据存储和检索。它提供高性能压缩和编码方案(encoding schemes)来批量处理复杂数据,并且受到许多编程语言和分析工具的支持。 在我们通过flink写入parquet文件的时候,会遇到timestamp时间格式写入的问题。…...
redis实现lamp架构缓存
redis服务器环境下mysql实现lamp架构缓存 ip角色环境192.168.242.49缓存服务器Redis2.2.7192.168.242.50mysql服务器mysql192.168.242.51web端php ***默认已安装好redis,mysql 三台服务器时间同步(非常重要) # 下载ntpdate yum -y install…...
正则表达式中常见的贪婪词
1. * 含义:匹配前面的元素零次或者多次。示例:对于正则表达式 a*,在字符串 "aaaa" 中,它会匹配整个 "aaaa",因为它会尽可能多地匹配 a 字符。代码示例(Python):…...
CF 339A.Helpful Maths(Java实现)
题目分析 输入一串式子,输出从小到大排列的式子 思路分析 如上所说核心思路,但是我要使用笨方法,输入一串式子用split分割开,但是此时需要用到转义字符,即函数内参数不能直接使用“”,而是“\\”。分割开后…...
SQL 指南
SQL 指南 引言 SQL(Structured Query Language,结构化查询语言)是一种用于管理关系数据库系统的标准计算机语言。自1970年代问世以来,SQL已经成为了数据库管理和数据操作的事实标准。本文旨在为初学者和有经验的数据库用户提供一个全面的SQL指南,涵盖SQL的基础知识、高级…...
DDD架构实战第七讲总结:分层模型和代码组织
云架构师系列课程之DDD架构实战第七讲总结:分层模型和代码组织 一、引言 在前几讲中,我们介绍了领域驱动设计(DDD)的基本构造块和生命周期模型中的聚合。本讲将重点讨论如何将这些构造块和代码组织起来,探讨分层架构和六边形模型,以及如何组织代码结构。 二、工厂和资…...
Python “字典” 实战案例:5个项目开发实例
Python “字典” 实战案例:5个项目开发实例 内容摘要 本文包括 5 个使用 Python 字典的综合应用实例。具体是: 电影推荐系统配置文件解析器选票统计与排序电话黄页管理系统缓存系统(LRU 缓存) 以上每一个实例均有完整的程序代…...
(一)QT的简介与环境配置WIN11
目录 一、QT的概述 二、QT的下载 三、简单编程 常用快捷键 一、QT的概述 简介 Qt(发音:[kjuːt],类似“cute”)是一个跨平台的开发库,主要用于开发图形用户界面(GUI)应用程序,…...
在 Windows 系统上,将 Ubuntu 从 C 盘 迁移到 D 盘
在 Windows 系统上,如果你使用的是 WSL(Windows Subsystem for Linux)并安装了 Ubuntu,你可以将 Ubuntu 从 C 盘 迁移到 D 盘。迁移过程涉及导出当前的 Ubuntu 发行版,然后将其导入到 D 盘的目标目录。以下是详细的步骤…...
vue2的$el.querySelector在vue3中怎么写
这个也属于直接操作 dom 了,不建议在项目中这样操作,不过我是在vue2升级vue3的时候遇到的,是以前同事写的代码,也没办法 先来看一下对比 在vue2中获取实例是直接通过 this.$refs.xxx 获取绑定属性 refxxx 的实例,并且…...
GPSd定时检测保活TCP GPS源
为了在 TCP GPS 源丢失连接时自动重新连接,可以编写一个监控脚本,定期检查 gpspipe 输出中的 TCP 源数据是否存在。如果检测到丢失,则使用 gpsdctl 或直接命令重新添加 TCP 源。 1、工具 检查并安装必要工具,本例需要使用 gpspi…...
IDEA中Maven使用的踩坑与最佳实践
文章目录 IDEA中Maven使用的踩坑与最佳实践一、环境配置类问题1. Maven环境配置2. IDEA中Maven配置建议 二、常见问题与解决方案1. 依赖下载失败2. 依赖冲突解决3. 编译问题修复 三、效率提升技巧1. IDEA Maven Helper插件使用2. 常用Maven命令配置3. 多模块项目配置4. 资源文件…...
使用 Python 调用 OpenAI 的接口初识
使用 Python 调用 OpenAI 的接口非常简单,以下将结合实际代码示例和使用场景进行详细讲解,步骤如下: 文章目录 1. 安装 OpenAI 官方库2. 准备 API Key3. 基本使用示例:调用 ChatGPT**代码示例:****运行结果:…...
2025 最新flutter面试总结
目录 1.Dart是值传递还是引用传递? 2.Flutter 是单引擎还是双引擎 3. StatelessWidget 和 StatefulWidget 在 Flutter 中有什么区别? 4.简述Dart语音特性 5. Navigator 是什么?在 Flutter 中 Routes 是什么? 6、Dart 是不是…...
【MQ】RabbitMq的可靠性保证
消息队列中的可靠性主要是分为三部分: 消息不丢失:确保消息从生产者发送到消费者消息不丢失消息不重复:确保消息不被重复消费消息顺序性:确保消费的顺序性 解决方案主要有以下几部分: 消息不丢失 生产者确认机制持久…...
STM32 GPIO配置 点亮LED灯
本次是基于STM32F407ZET6做一个GPIO配置,实现点灯实验。 新建文件 LED.c、LED.h文件,将其封装到Driver文件中。 双击Driver文件将LED.c添加进来 编写头文件,这里注意需要将Driver头文件声明一下。 在LED.c、main.c里面引入头文件LED.h LED初…...
Flink把kafa数据写入Doris的N种方法及对比。
用Flink+Doris来开发实时数仓,首要解决是如何接入kafka实时流,下面是参考Doris官方文档和代码,在自己项目开发的实践中总结,包括一些容易踩坑的细节。 目录 Routine Load方法 接入kafka实时数据 踩坑的问题细节 Flink Doris Connector方法 完整示例 Routine Load方法…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...
Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...
