当前位置: 首页 > news >正文

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

目录

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

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

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)


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

相关文章:

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

目录 一、什么是动态规划&#xff1f;—— 从人类直觉到算法思维 二、暴力递归&#xff1a;最直观的问题分解方式 1. 示例&#xff1a;斐波那契数列 2. 递归树分析&#xff08;以n5为例&#xff09; 3. 问题暴露 三、第一次优化&#xff1a;记忆化搜索&#xff08;Memoiza…...

前端学习——HTML

HTML VSCode常用快捷键HTML标签文本标签列表标签表格Form表单表单元素 块元素与行内元素新增标签 VSCode常用快捷键 代码格式化&#xff1a;ShiftAltF 向上或向下移动一行&#xff1a;AltUp或AltDown 快速复制一行代码&#xff1a;ShiftAltUp或者ShiftAltDown 快速替换&#x…...

12.【线性代数】——图和网络

十二 图和网络&#xff08;线性代数的应用&#xff09; 图 g r a p h { n o d e s , e d g e s } graph\{nodes, edges\} graph{nodes,edges}1.关联矩阵2. A A A矩阵的零空间&#xff0c;求解 A x 0 Ax0 Ax0 电势3. A T A^T AT矩阵的零空间&#xff0c;电流总结电流图结论 …...

[环境搭建篇] Windows 环境下如何安装repo工具

Windows 环境下如何安装repo工具 1. 安装前置依赖2. 配置Repo引导脚本方法一&#xff1a;通过Gitee镜像安装&#xff08;推荐&#xff09;方法二&#xff1a;通过清华镜像安装 3. 解决依赖问题4. 初始化Repo仓库5. 常见问题解决 前言&#xff1a; 在Windows环境下安装Repo工具需…...

LeetCode 热题 100_字符串解码(71_394_中等_C++)(栈)

LeetCode 热题 100_字符串解码&#xff08;71_394&#xff09; 题目描述&#xff1a;输入输出样例&#xff1a;题解&#xff1a;解题思路&#xff1a;思路一&#xff08;栈&#xff09;&#xff1a; 代码实现代码实现&#xff08;栈&#xff09;&#xff1a;以思路一为例进行调…...

「DataX」数据迁移-IDEA运行DataX方法总结

背景 业务需求希望把Oracle数据库中的数据&#xff0c;迁移至MySql数据库中&#xff0c;因为需要迁移全量和增量的数据&#xff0c;所以希望想用数据迁移工具进行操作。 经过一些调研查询&#xff0c;最终打算使用DataX进行数据的迁移。 DataX简单介绍 DataX 是阿里云 DataW…...

【 <一> 炼丹初探:JavaWeb 的起源与基础】之 Servlet 过滤器:实现请求的预处理与后处理

<前文回顾> 点击此处查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、过滤器&…...

DeepSeek与浏览器自动化AI Agent构建指南

文章使用到的模型可以用硅基流动中的&#xff1a; 注册链接&#xff1a;硅基流动统一登录 邀请码&#xff1a;FytHp9Xa 一、技术选型阶段 1. 基础组件选择 AI模型&#xff1a;DeepSeek-R1开放API&#xff08;对话/推理&#xff09;或DeepSeek-Coder&#xff08;代码生成&#…...

面试中常问的mysql数据库指令【杭州多测师_王sir】

数据库中的修改表结构、增删改查、用户权限操作DDL 》数据库定义语言 create database&#xff0c;create table drop tableDML 》数据库操作语言 insert into&#xff0c;delete from&#xff0c;update set&#xff0c;DQL 》数据库查询语言 select .... from....crea…...

深度学习驱动的智能化革命:从技术突破到行业实践

第一章 深度学习的技术演进与核心架构 1.1 从浅层网络到深度学习的范式转变 深度学习的核心在于通过多层次非线性变换自动提取数据特征,其发展历程可划分为三个阶段:符号主义时代的规则驱动(1950s-1980s)、连接主义时代的浅层网络(1990s-2000s)以及深度学习时代的端到端…...

基于编译器特性浅析C++程序性能优化

最近在恶补计算机基础知识&#xff0c;学到CSAPP第五章的内容&#xff0c;在这里总结并且展开一下C程序性能优化相关的内容。 衡量程序性能的方式 一般而言&#xff0c;程序的性能可以用CPE&#xff08;Cycles Per Element&#xff09;来衡量&#xff0c;其指的是处理每个元素…...

服务器上通过ollama部署deepseek

2025年1月下旬&#xff0c;DeepSeek的R1模型发布后的一周内就火了&#xff0c;性能比肩OpenAI的o1模型&#xff0c;且训练成本仅为560万美元&#xff0c;成本远低于openAI&#xff0c;使得英伟达股票大跌。 下面我们来看下如何个人如何部署deepseek-r1模型。 我是用的仙宫云的…...

Android Coil总结

文章目录 Android Coil总结概述添加依赖用法基本用法占位图变形自定义ImageLoader取消加载协程支持缓存清除缓存监听 简单封装 Android Coil总结 概述 Coil 是一个用于 Android 的 Kotlin 图像加载库&#xff0c;旨在简化图像加载和显示的过程。它基于 Kotlin 协程&#xff0…...

《安富莱嵌入式周报》第351期:DIY半导体制造,工业设备抗干扰提升方法,NASA软件开发规范,小型LCD在线UI编辑器,开源USB PD电源,开源锂电池管理

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 视频版&#xff1a; https://www.bilibili.com/video/BV16C95YEEZs 《安富莱嵌入式周报》第351期&#xff1a;DIY半导体…...

Redis在人员管理系统中的应用示例

用户会话管理 场景&#xff1a;用户登录后存储会话信息&#xff0c;支持多服务器共享 实现&#xff1a; 用户登录成功后&#xff0c;生成唯一Token&#xff08;如JWT&#xff09;&#xff0c;作为Redis的Key Value存储用户ID、角色、权限等信息&#xff0c;设置过期时间&…...

The Wedding Juicer POJ - 2227

采取从外层边界&#xff0c;一步一步向内部拓展的策略&#xff0c;具体来说&#xff0c;一开始将最外面一层的点加入队列&#xff0c;并标记这些点的坐标已经被访问 取出队列中高度最低的点&#xff0c;将其弹出&#xff0c;查看其上下左右的点&#xff0c;如果新点没有被访问…...

# 深入理解RNN(一):循环神经网络的核心计算机制

深入理解RNN&#xff1a;循环神经网络的核心计算机制 RNN示意图 引言 在自然语言处理、时间序列预测、语音识别等涉及序列数据的领域&#xff0c;循环神经网络(RNN)一直扮演着核心角色。尽管近年来Transformer等架构逐渐成为主流&#xff0c;RNN的基本原理和思想依然对于理…...

分布式锁—6.Redisson的同步器组件

大纲 1.Redisson的分布式锁简单总结 2.Redisson的Semaphore简介 3.Redisson的Semaphore源码剖析 4.Redisson的CountDownLatch简介 5.Redisson的CountDownLatch源码剖析 1.Redisson的分布式锁简单总结 (1)可重入锁RedissonLock (2)公平锁RedissonFairLock (3)联锁MultiL…...

同步 Fork 仓库的命令

同步 Fork 仓库的命令 要将您 fork 的仓库的 main 分支与原始仓库&#xff08;fork 源&#xff09;同步&#xff0c;您可以使用以下命令&#xff1a; 首先&#xff0c;确保您已经添加了原始仓库作为远程仓库&#xff08;如果尚未添加&#xff09;&#xff1a; git remote add…...

基于PySide6的CATIA零件自动化着色工具开发实践

引言 在汽车及航空制造领域&#xff0c;CATIA作为核心的CAD设计软件&#xff0c;其二次开发能力对提升设计效率具有重要意义。本文介绍一种基于Python的CATIA零件着色工具开发方案&#xff0c;通过PySide6实现GUI交互&#xff0c;结合COM接口操作实现零件着色自动化。该方案成…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板&#xff0c;就像一个模具&#xff0c;里面可以将不同类型的材料做成一个形状&#xff0c;其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式&#xff1a;templa…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

JavaScript基础-API 和 Web API

在学习JavaScript的过程中&#xff0c;理解API&#xff08;应用程序接口&#xff09;和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能&#xff0c;使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

多模态图像修复系统:基于深度学习的图片修复实现

多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...