动态规划详解(二):从暴力递归到动态规划的完整优化之路

目录

一、什么是动态规划?—— 从人类直觉到算法思维

二、暴力递归:最直观的问题分解方式

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. 五步法流程

  1. 定义状态:明确dp数组的含义

  2. 推导转移方程:分析状态间的关系

  3. 初始化:设置边界条件

  4. 确定遍历顺序:保证前置状态已计算

  5. 输出结果:从dp数组中提取答案

2. 优化路线图


3. 常见问题处理技巧

  • 边界条件处理:增加虚拟头节点(如dp[0])

  • 路径记录:使用额外数组保存选择路径

  • 维度压缩:滚动数组、位运算优化


八、实战练习建议

  1. 基础练习

    • LeetCode 70. 爬楼梯(空间优化)

    • LeetCode 118. 杨辉三角(二维DP)

  2. 进阶挑战

    • LeetCode 322. 零钱兑换(完全背包)

    • LeetCode 1143. 最长公共子序列(二维字符串DP)


掌握动态规划的关键在于将递归思维转化为状态转移思维。建议从简单问题入手,逐步体会"重叠子问题"的特征,最终达到看到问题就能自然拆分状态的境界。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

下降路径最⼩和(medium)

题目描述&#xff1a; 给你一个 n x n 的 方形 整数数组 matrix &#xff0c;请你找出并返回通过 matrix 的下降路径 的 最小和 。 下降路径 可以从第一行中的任何元素开始&#xff0c;并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列&#xff08…

YashanDB认证,YCA证书认证教程,免费证书,内含真题考试题库及答案——五分钟速成

目录 一.账号及平台注册登录流程 二.登录进行设备调试核验 三.考试&#xff08;考完获取分数&#xff09; 四.获取证书 五.题库及答案 一.账号及平台注册登录流程 1-点击这里进行账号注册&#xff08;首次学习必须先注册&#xff0c;有账号之后可以直接在2号链接登录&#…

texstudio: 编辑器显示行号+给PDF增加行号

texstudio在编辑器部分增加行号&#xff1a; texstudio默认在编辑器部分不显示行号&#xff0c;如下图&#xff1a; 要实现以下的在编辑部分增加行号&#xff1a; 执行如下操作&#xff1a; 选项-->设置TexStudio-->编辑器-->显示行号-->所有行号选择好后&…

解决vscode中出现“无法将pip项识别...“问题

问题 遇见问题如下&#xff1a; 查看pip 通过 winR &#xff0c;输入 cmd&#xff0c;进入终端&#xff0c;搜索 where pip。 发现 pip 查不出来&#xff0c;然后进入文件资源管理器&#xff0c;搜索 Scripts 文件夹&#xff0c;如果没有找到可能是电脑没有下载 python。 点击…

【webrtc debug tools】 rtc_event_log_to_text

一、rtc_event_log 简介 在学习分析webrtc的过程中&#xff0c;发现其内部提供了一个实时数据捕获接口RtcEventLog。通过该接口可以实时捕获进出webrtc的RTP报文头数据、音视频配置参数、webrtc的探测数据等。其内容实现可参考RtcEventLogImpl类的定义。其文件所在路径 loggin…

华为eNSP:2.配置OSPF报文分析和验证

一、OSPF的5种数据包 Hello包&#xff1a;用于发现和维护邻居关系。定期发送&#xff0c;确保邻居路由器在线。 数据库描述包&#xff08;DBD, Database Description Packet&#xff09;&#xff1a;在邻居关系建立后&#xff0c;用于交换链路状态数据库的摘要信息。 链路状…

初次体验Tauri和Sycamore(3)通道实现

​ 原创作者&#xff1a;庄晓立&#xff08;LIIGO&#xff09; 原创时间&#xff1a;2025年03月10日&#xff08;发布时间&#xff09; 原创链接&#xff1a;https://blog.csdn.net/liigo/article/details/146159327 版权所有&#xff0c;转载请注明出处。 20250310 LIIGO备注&…

DBeaver安装教程+连接TDengine数据库

为TDengine安装的DBeaver教程 安装 23.1.1 版本以上的DBeaver 因为官方文档说这个版本之上的DBeaver才支持TDengine内嵌前往DBeaver 官方文档进行版本下载滑到链接最下面点击进入 点击download&#xff0c;进入选择下载版本 等待下载成功即可双击自行安装 打开数据库连接TDen…

Java 学习记录:基础到进阶之路(一)

今天&#xff0c;让我们深入到 Java 项目构建、基础语法及核心编程概念的领域&#xff0c;一探究竟。 软件安装及环境配置请查看之前更新的博客有着详细的介绍&#xff1a; IDEA软件安装&环境配置&中文插件-CSDN博客 目录 1.Java 项目构建基础 1.项目中的 SRC 目录…

【蓝桥杯】每天一题,理解逻辑(3/90)【Leetcode 快乐数】

闲话系列&#xff1a;每日一题&#xff0c;秃头有我&#xff0c;Hello&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;,我是IF‘Maxue&#xff0c;欢迎大佬们来参观我写的蓝桥杯系列&#xff0c;我好久没有更新博客了&#xff0c;因为up猪我寒假用自己的劳动换了…

STM32Cubemx-H7-7-OLED屏幕

如何把江科大的OLED标准库文件换成hal库的文件 前言 本文讲解如在hHAL库中使用OLED&#xff0c;其实江科大做的文件好已经很好了 只讲操作&#xff0c;不讲废话&#xff0c;默认大家都有32基本操作 创建工程 首先创建工程 把那两个引脚设置成开漏 获取标准库文件 打开江科大O…

基于 Vue 的Deepseek流式加载对话Demo

目录 引言组件概述核心组件与功能实现1. 消息显示组件&#xff08;Message.vue&#xff09;2. 输入组件&#xff08;Input.vue&#xff09;3. 流式请求处理&#xff08;useDeepseek.ts&#xff09;4. 语音处理模块&#xff08;Voice.vue&#xff09; 总结Demo Github 地址 引言…

Pixelmator Pro for Mac 专业图像处理软件【媲美PS的修图】

介绍 Pixelmator Pro&#xff0c;是一款非常强大、美观且易于使用的图像编辑器&#xff0c;专为 Mac 设计。采用单窗口界面、基于机器学习的智能图像编辑、自动水平检测&#xff0c;智能快速选择及更好的修复工具等功能优点。许多非破坏性的专业编辑工具可让您进行最佳的照片处…

YOLO结合bytetrack对车辆目标跟踪计数

本文采用YOLOv8作为核心算法框架&#xff0c;结合PyQt5构建用户界面&#xff0c;使用Python3进行开发。YOLOv8以其高效的实时检测能力&#xff0c;在多个目标检测任务中展现出卓越性能。本研究针对车辆目标数据集进行训练和优化&#xff0c;该数据集包含丰富的车辆目标图像样本…

通义万相2.1 图生视频:为AI绘梦插上翅膀,开启ALGC算力领域新纪元

通义万相2.1图生视频大模型 通义万相2.1图生视频技术架构万相2.1的功能特点性能优势与其他工具的集成方案 蓝耘平台部署万相2.1核心目标典型应用场景未来发展方向 通义万相2.1ALGC实战应用操作说明功能测试 为什么选择蓝耘智算蓝耘智算平台的优势如何通过API调用万相2.1 写在最…

软考中级_【软件设计师】知识点之【知识产权】

简介 知识产权模块主要涉及软件行业相关法律保护体系&#xff0c;包括著作权、专利权、商标权及商业秘密等内容。重点涵盖软件著作权登记流程、源代码保护范围、专利创新性认定标准&#xff0c;以及开源协议&#xff08;如GPL、MIT&#xff09;的法律约束力。考生需掌握**《计算…

Kafka×DeepSeek:智能决策破取经八十一难!

《西游记》的故事中&#xff0c;唐僧师徒四人历经九九八十一难&#xff0c;从东土大唐前往西天取经。一路上&#xff0c;火焰山酷热难耐、通天河水位忽高忽低、妖怪神出鬼没…… 现在&#xff0c;唐僧师徒取经路上的种种难题&#xff0c;在KafkaDeepSeek双引擎加持下有了全新解…

nextjs15使用next-intl实现国际化多语言

在nextjs15当中使用next-intl可以轻松实现国际化&#xff0c;本文将着重阐述&#xff0c;如何在nextjs15使用next-intl。 一、创建项目安装依赖 1、创建nextjs项目 pnpm dlx create-next-app my-app 2、安装next-intl pnpm add next-intl 二、创建组件文件 1、项目结构 …

【C++模板】:开启泛型编程之门(函数模版,类模板)

&#x1f4dd;前言&#xff1a; 在上一篇文章C内存管理中我们介绍了C的内存管理&#xff0c;重点介绍了与C语言的区别&#xff0c;以及new和delete。这篇文章我们将介绍C的利器——模板。 在C编程世界里&#xff0c;模板是一项强大的特性&#xff0c;它为泛型编程奠定了坚实基础…

Android : Camera之CHI API

来自&#xff1a; https://www.cnblogs.com/szsky/articles/10861918.html 一、CAM CHI API功能介绍&#xff1a; CHI API建立在Google HAL3的灵活性基础之上&#xff0c;目的是将Camera2/HAL3接口分离出来用于使用相机功能&#xff0c;它是一个灵活的图像处理驱动程序&#…