动态规划详解(二):从暴力递归到动态规划的完整优化之路
目录
一、什么是动态规划?—— 从人类直觉到算法思维
二、暴力递归:最直观的问题分解方式
1. 示例:斐波那契数列
2. 递归树分析(以n=5为例)
3. 问题暴露
三、第一次优化:记忆化搜索(Memoization)
1. 核心思想
2. 斐波那契优化实现
3. 复杂度分析
四、第二次进化:自底向上动态规划
1. 思维转变
2. 斐波那契DP实现
3. 空间优化(滚动数组)
五、经典案例:爬楼梯问题(LeetCode 70)
1. 问题描述
2. 暴力递归解法
3. DP优化实现
4. 状态转移方程推导
六、高阶案例:0-1背包问题
1. 问题描述
2. 暴力递归解法
3. 记忆化搜索优化
4. 动态规划终极形态
5. 空间压缩技巧(滚动数组)
七、动态规划解题方法论总结
1. 五步法流程
2. 优化路线图
3. 常见问题处理技巧
八、实战练习建议
一、什么是动态规划?—— 从人类直觉到算法思维
动态规划(Dynamic Programming, DP) 本质是一种通过"聪明的穷举"解决问题的思想。它的核心是发现重叠子问题和最优子结构,并通过存储中间结果避免重复计算。我们可以通过一个认知升级路线来理解它:
暴力递归 → 发现重复计算 → 记忆化搜索 → 推导状态转移 → 自底向上动态规划
二、暴力递归:最直观的问题分解方式
1. 示例:斐波那契数列
// 经典递归实现
public int fib(int n) {if (n <= 1) return n;return fib(n-1) + fib(n-2);
}
2. 递归树分析(以n=5为例)
fib(5)/ \fib(4) fib(3)/ \ / \
fib(3) fib(2) fib(2) fib(1)
...(继续展开)...
3. 问题暴露
重复计算:fib(3)计算2次,fib(2)计算3次
指数级复杂度:O(2^n) 时间复杂度,O(n) 栈空间
三、第一次优化:记忆化搜索(Memoization)
1. 核心思想
-
空间换时间:使用数组/HashMap存储已计算结果
-
剪枝优化:计算前先查询存储结构
2. 斐波那契优化实现
public int fibMemo(int n) {int[] memo = new int[n+1];Arrays.fill(memo, -1);return dfs(n, memo);
}private int dfs(int n, int[] memo) {if (n <= 1) return n;if (memo[n] != -1) return memo[n];memo[n] = dfs(n-1, memo) + dfs(n-2, memo);return memo[n];
}
3. 复杂度分析
时间复杂度:O(n) —— 每个子问题只计算一次
空间复杂度:O(n) 递归栈 + O(n) 存储空间
四、第二次进化:自底向上动态规划
1. 思维转变
递归(自顶向下) → 迭代(自底向上)
2. 斐波那契DP实现
public int fibDP(int n) {if (n <= 1) return n;int[] dp = new int[n+1];dp[0] = 0;dp[1] = 1;for (int i = 2; i <= n; i++) {dp[i] = dp[i-1] + dp[i-2]; // 状态转移方程}return dp[n];
}
3. 空间优化(滚动数组)
public int fibOpt(int n) {if (n <= 1) return n;int prev = 0, curr = 1;for (int i = 2; i <= n; i++) {int sum = prev + curr;prev = curr;curr = sum;}return curr;
}
五、经典案例:爬楼梯问题(LeetCode 70)
1. 问题描述
每次可以爬1或2个台阶,求到达第n阶的不同方法数
2. 暴力递归解法
public int climbStairs(int n) {if (n == 1) return 1;if (n == 2) return 2;return climbStairs(n-1) + climbStairs(n-2);
}
3. DP优化实现
public int climbStairsDP(int n) {if (n <= 2) return n;int[] dp = new int[n+1];dp[1] = 1;dp[2] = 2;for (int i = 3; i <= n; i++) {dp[i] = dp[i-1] + dp[i-2];}return dp[n];
}
4. 状态转移方程推导
dp[i] = dp[i-1] + dp[i-2]
解释:到达第i阶的方法数 = 从i-1阶上1步 + 从i-2阶上2步
六、高阶案例:0-1背包问题
1. 问题描述
给定物品重量w[]和价值v[],背包容量C,求最大价值
2. 暴力递归解法
public int knapsack(int[] w, int[] v, int C) {return dfs(w, v, w.length-1, C);
}private int dfs(int[] w, int[] v, int index, int cap) {if (index < 0 || cap <= 0) return 0;// 不选当前物品int no = dfs(w, v, index-1, cap);// 选当前物品(前提:容量足够)int yes = cap >= w[index] ? dfs(w, v, index-1, cap - w[index]) + v[index] : 0;return Math.max(no, yes);
}
3. 记忆化搜索优化
public int knapsackMemo(int[] w, int[] v, int C) {int n = w.length;int[][] memo = new int[n][C+1];return dfs(w, v, n-1, C, memo);
}private int dfs(int[] w, int[] v, int index, int cap, int[][] memo) {if (index < 0 || cap <= 0) return 0;if (memo[index][cap] != 0) return memo[index][cap];int no = dfs(w, v, index-1, cap, memo);int yes = cap >= w[index] ? dfs(w, v, index-1, cap - w[index], memo) + v[index] : 0;memo[index][cap] = Math.max(no, yes);return memo[index][cap];
}
4. 动态规划终极形态
public int knapsackDP(int[] w, int[] v, int C) {int n = w.length;int[][] dp = new int[n+1][C+1];for (int i = 1; i <= n; i++) {for (int j = 1; j <= C; j++) {if (j < w[i-1]) {dp[i][j] = dp[i-1][j]; // 装不下} else {dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j - w[i-1]] + v[i-1]);}}}return dp[n][C];
}
5. 空间压缩技巧(滚动数组)
public int knapsackOpt(int[] w, int[] v, int C) {int[] dp = new int[C+1];for (int i = 0; i < w.length; i++) {for (int j = C; j >= w[i]; j--) { // 必须倒序遍历dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);}}return dp[C];
}
七、动态规划解题方法论总结
1. 五步法流程
-
定义状态:明确dp数组的含义
-
推导转移方程:分析状态间的关系
-
初始化:设置边界条件
-
确定遍历顺序:保证前置状态已计算
-
输出结果:从dp数组中提取答案
2. 优化路线图
3. 常见问题处理技巧
-
边界条件处理:增加虚拟头节点(如dp[0])
-
路径记录:使用额外数组保存选择路径
-
维度压缩:滚动数组、位运算优化
八、实战练习建议
-
基础练习
-
LeetCode 70. 爬楼梯(空间优化)
-
LeetCode 118. 杨辉三角(二维DP)
-
-
进阶挑战
-
LeetCode 322. 零钱兑换(完全背包)
-
LeetCode 1143. 最长公共子序列(二维字符串DP)
-
掌握动态规划的关键在于将递归思维转化为状态转移思维。建议从简单问题入手,逐步体会"重叠子问题"的特征,最终达到看到问题就能自然拆分状态的境界。
相关文章:
动态规划详解(二):从暴力递归到动态规划的完整优化之路
目录 一、什么是动态规划?—— 从人类直觉到算法思维 二、暴力递归:最直观的问题分解方式 1. 示例:斐波那契数列 2. 递归树分析(以n5为例) 3. 问题暴露 三、第一次优化:记忆化搜索(Memoiza…...
ubuntu下在pycharm中配置已有的虚拟环境
作者使用的ubuntu系统位于PC机上的虚拟机。系统版本为: 在配置pycharm解释器之前你需要先创建虚拟环境以及安装pycharm。 作者创建的虚拟环境位于/home/topeet/miniconda3/envs/airproject/,如下图所示: 作者安装的pycharm版本为2023社区…...
虚幻基础:动画层接口
文章目录 动画层:动画图表中的函数接口:名字,没有实现。动画层接口:由动画蓝图实现1.动画层可直接调用实现功能2.动画层接口必须安装3.动画层默认使用本身实现4.动画层也可使用其他动画蓝图实现,但必须在角色蓝图中关联…...
deepseek R1提供的3d迷宫设计方案
一、技术选型方案 核心渲染技术 🎨 采用Raycasting算法模拟3D透视效果使用Canvas 2D上下文进行逐像素绘制材质贴图系统实现墙面差异化表现 迷宫数据结构 🗺️ 二维数组存储迷宫布局(0:通路,1:墙体)递归回溯算法生成随…...
2024华为OD机试真题-日志排序(C++/Java/Python)-E卷-100分
2024华为OD机试最新E卷题库-(C卷+D卷+E卷)-(JAVA、Python、C++) 目录 题目描述 输入描述 输出描述 示例1 示例2 示例3 解题思路 代码 c++ python Java 题目描述 运维工程师采集到某产品现网运行一天产生的日志N条,现需根据日志时间按时间先后顺序对日志进行排序。…...
爬虫中一些有用的用法
文本和标签在一个级别下 如果文本和a标签在一个级别下 比如: # 获取a标签后的第一个文本节点text_node a.xpath(following-sibling::text()[1])[0].strip() 将xpath的html代码转换成字符串 etree.tostring(root, pretty_printTrue, encoding"utf-8")…...
Python控制语句-条件分支-if
1.以下代码的输出结果是()。 for i in range(1,6): if i%4= =0: continue else: print(i, end=“,”) A、1,2,3 B、1,2,3,4 C、1,2,3,5, D、1,2,3,5,6 答案:C。for循环依次将1-6依次赋给i,i从1,2,3,4,5依次变化,当i%4= =0时,结束本次循环进入下一循环;反之输出的值,故输出…...
DeepIn Wps 字体缺失问题
系统缺失字体 Symbol 、Wingdings 、Wingdings2、Wingdings3、MT—extra 字体问题 问了下DeepSeek 在应用商店安装或者在windows 里面找 装了一个GB-18030 还是不行 在windows里面复制了缺失的字体 将字体复制到DeepIn 的字体目录(Ubuntu 应该也是这个目录&am…...
【webrtc debug tools】 rtc_event_log_to_text
一、rtc_event_log 简介 在学习分析webrtc的过程中,发现其内部提供了一个实时数据捕获接口RtcEventLog。通过该接口可以实时捕获进出webrtc的RTP报文头数据、音视频配置参数、webrtc的探测数据等。其内容实现可参考RtcEventLogImpl类的定义。其文件所在路径 loggin…...
数字IC后端项目典型问题(2025.03.10数字后端项目问题记录)
小编发现今天广大学员发过来的问题都比较好,立即一顿输出分享给大家(每天都有好多种类的数字后端问题)。后续可能会经常通过这种方式来做分享。其实很多问题都是实际后端项目中经常遇到的典型问题。希望通过这种方式的分享能够帮助到更多需要…...
我与DeepSeek读《大型网站技术架构》(10)- 维基百科的高性能架构设计分析
目录 网站整体架构核心组件请求处理流程图关键环节说明 性能优化策略前端优化:拦截 80% 以上请求服务端优化:高性能 PHP 集群后端优化:存储与缓存极致设计Memcached 持久化连接 性能优化策略对比表 网站整体架构 核心组件 Wikipedia 的架构…...
Redis 持久化详解:RDB 与 AOF 的机制、配置与最佳实践
目录 引言 1. Redis 持久化概述 1.1 为什么需要持久化? 1.2 Redis 持久化的两种方式 2. RDB 持久化 2.1 RDB 的工作原理 RDB 的触发条件 2.2 RDB 的配置 2.3 RDB 的优缺点 优点 缺点 3. AOF 持久化 3.1 AOF 的工作原理 AOF 的触发条件 3.2 AOF 的配置…...
说一下spring的事务隔离级别?
大家好,我是锋哥。今天分享关于【说一下spring的事务隔离级别?】面试题。希望对大家有帮助; 说一下spring的事务隔离级别? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Spring的事务隔离级别是指在数据库事务管理中…...
Java 大视界 -- 基于 Java 的大数据实时数据处理框架性能评测与选型建议(121)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
深度解读:OpenAI发布GPT-5的技术突破与商业影响
引言 2025年2月,OpenAI正式发布GPT-5,这一被誉为“AI新纪元开篇之作”的模型,不仅实现了技术架构的颠覆性创新,更以免费开放策略引发行业地震。本文将从技术突破、商业影响、行业竞争格局及未来挑战四个维度,全面解析…...
NAT NAPT
NAT NAT(Network Address Translation,网络地址转换) 主要用于在不同网络(如私有网络和公共互联网)之间进行 IP 地址转换,解决IP 地址短缺问题,并提供一定的安全性。 IPv4 地址是 32 位…...
harmonyOS(鸿蒙)— 网络权限(解决app网络资源无法加载,图片无法显示)
harmonyOS系列 harmonyOS(网络权限) 一、问题梳理二、代码及图示 一、问题梳理 在鸿蒙app的开发里会有联网业务无法加载,图片无法显示的情况,多半是系统的网络权限没有申请,所以无法使用需要网络加载的资源࿰…...
Python毕业设计选题:基于django+vue的疫情数据可视化分析系统
开发语言:Python框架:djangoPython版本:python3.7.7数据库:mysql 5.7数据库工具:Navicat11开发软件:PyCharm 系统展示 管理员登录 管理员功能界面 用户管理 员工管理 疫情信息管理 检测预约管理 检测结果…...
小程序配置webview
1.在微信公众平台配置业务域名 1)包括把校验文件放在服务器根目录 2)配置域名 2.在小程序中 新建文件 小程序新建页面:web-view json配置:{ "pageOrientation": "landscape", "renderer":&qu…...
清华同方国产电脑能改windows吗_清华同方国产系统改win7教程
清华同方国产电脑能改windows吗?清华同方国产电脑如果采用的是兆芯kx-6000系列或kx-7000系列以及海光c86 3250 3350 X86架构处理器可以安装windows。在安装win7时bios中要关闭“安全启动”和开启legacy传统模式支持,如果是NVME接口的固态硬盘,…...
【redis】string应用场景:缓存功能和计数功能
文章目录 缓存功能实现思路存在的问题伪代码实现 记数功能实现思路统计伪代码实现 缓存功能 实现思路 整体的思路: 应用服务器访问数据的时候,先查询 Redis 如果 Redis 上数据存在了,就直接从 Redis 读取数据交给应用服务器,不继…...
vue el-select 省市区三级联动 vue + element-ui使用第三方插件实现省市区三级联动
vue el-select 省市区三级联动 vue使用第三方插件实现省市区三级联动 网上找了好多教程,都是使用el-cascader级联选择器的省市区选择器,但是几乎没有三个单独的el-select的进行关联的三级省市联动组件效果 第一步:先安装省市区element-ui的插件 npm install element-china-a…...
数学建模:MATLAB强化学习
一、强化学习简述 强化学习是一种通过与环境交互,学习状态到行为的映射关系,以获得最大积累期望回报的方法。包含环境,动作和奖励三部分,本质是智能体通过与环境的交互,使得其作出的动作所得到的决策得到的总的奖励达…...
从0开始的操作系统手搓教程45——实现exec
目录 建立抽象 实现加载 实现sys_execv !!!提示:因为实现问题没有测试。所以更像是笔记! exec 函数的作用是用新的可执行文件替换当前进程的程序体。具体来说,exec 会将当前正在运行的用户进程的进程体&…...
深入理解 Linux 中的 -h 选项:让命令输出更“人性化”
在 Linux 系统中,命令行工具是系统管理员和普通用户最常用的交互方式之一。然而,命令行输出往往充满了技术性术语和数字,对于初学者或非技术用户来说可能显得晦涩难懂。幸运的是,许多 Linux 命令都提供了一个非常实用的选项&#…...
23. 观察者模式
原文地址: 观察者模式 更多内容请关注:智想天开 1. 观察者模式简介 观察者模式(Observer Pattern)是一种行为型设计模式,用于建立对象之间的一种一对多的依赖关系。当一个对象的状态发生变化时,所有依赖于它的对象都…...
sql语句分页的关键字是?
在 SQL 中,分页通常是通过限制查询结果的数量并指定从哪一行开始获取数据来实现的。不同的数据库系统使用不同的分页关键字。 以下是常见数据库系统的分页关键字: MySQL / PostgreSQL / SQLite 使用 LIMIT 和 OFFSET 来进行分页: LIMIT 限…...
golang从入门到做牛马:第十四篇-Go语言结构体:数据的“定制容器”
在Go语言中,结构体是一种非常强大的数据结构,它允许你将不同类型的数据组合在一起,形成一个逻辑上的“记录”。结构体非常适合用来表示复杂的数据类型,比如一个图书馆的书籍记录、一个用户的信息等。接下来,让我们一起深入了解Go语言中的结构体。 什么是结构体:数据的“组…...
C#控制台应用程序学习——3.11
一、整型数字计算 如果我们想执行以下程序:程序提示用户输入一个数字并输出 num 20 的结果,我们的思维应该是这样的: using System;public class Class1 {public static void Main(string[] args){Console.WriteLine("Enter the first…...
【商城实战(13)】购物车价格与数量的奥秘
【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配…...
