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

news2025/3/13 2:08:57

目录

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

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

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.coloradmin.cn/o/2314031.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ubuntu下在pycharm中配置已有的虚拟环境

作者使用的ubuntu系统位于PC机上的虚拟机。系统版本为&#xff1a; 在配置pycharm解释器之前你需要先创建虚拟环境以及安装pycharm。 作者创建的虚拟环境位于/home/topeet/miniconda3/envs/airproject/&#xff0c;如下图所示&#xff1a; 作者安装的pycharm版本为2023社区…

爬虫中一些有用的用法

文本和标签在一个级别下 如果文本和a标签在一个级别下 比如&#xff1a; # 获取a标签后的第一个文本节点text_node a.xpath(following-sibling::text()[1])[0].strip() 将xpath的html代码转换成字符串 etree.tostring(root, pretty_printTrue, encoding"utf-8")…

DeepIn Wps 字体缺失问题

系统缺失字体 Symbol 、Wingdings 、Wingdings2、Wingdings3、MT—extra 字体问题 问了下DeepSeek 在应用商店安装或者在windows 里面找 装了一个GB-18030 还是不行 在windows里面复制了缺失的字体 将字体复制到DeepIn 的字体目录&#xff08;Ubuntu 应该也是这个目录&am…

【webrtc debug tools】 rtc_event_log_to_text

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

数字IC后端项目典型问题(2025.03.10数字后端项目问题记录)

小编发现今天广大学员发过来的问题都比较好&#xff0c;立即一顿输出分享给大家&#xff08;每天都有好多种类的数字后端问题&#xff09;。后续可能会经常通过这种方式来做分享。其实很多问题都是实际后端项目中经常遇到的典型问题。希望通过这种方式的分享能够帮助到更多需要…

Redis 持久化详解:RDB 与 AOF 的机制、配置与最佳实践

目录 引言 1. Redis 持久化概述 1.1 为什么需要持久化&#xff1f; 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的事务隔离级别?

大家好&#xff0c;我是锋哥。今天分享关于【说一下spring的事务隔离级别&#xff1f;】面试题。希望对大家有帮助&#xff1b; 说一下spring的事务隔离级别&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Spring的事务隔离级别是指在数据库事务管理中…

Java 大视界 -- 基于 Java 的大数据实时数据处理框架性能评测与选型建议(121)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

NAT NAPT

NAT NAT&#xff08;Network Address Translation&#xff0c;网络地址转换&#xff09; 主要用于在不同网络&#xff08;如私有网络和公共互联网&#xff09;之间进行 IP 地址转换&#xff0c;解决IP 地址短缺问题&#xff0c;并提供一定的安全性。 IPv4 地址是 32 位&#xf…

harmonyOS(鸿蒙)— 网络权限(解决app网络资源无法加载,图片无法显示)

harmonyOS系列 harmonyOS&#xff08;网络权限&#xff09; 一、问题梳理二、代码及图示 一、问题梳理 在鸿蒙app的开发里会有联网业务无法加载&#xff0c;图片无法显示的情况&#xff0c;多半是系统的网络权限没有申请&#xff0c;所以无法使用需要网络加载的资源&#xff0…

Python毕业设计选题:基于django+vue的疫情数据可视化分析系统

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 用户管理 员工管理 疫情信息管理 检测预约管理 检测结果…

清华同方国产电脑能改windows吗_清华同方国产系统改win7教程

清华同方国产电脑能改windows吗&#xff1f;清华同方国产电脑如果采用的是兆芯kx-6000系列或kx-7000系列以及海光c86 3250 3350 X86架构处理器可以安装windows。在安装win7时bios中要关闭“安全启动”和开启legacy传统模式支持&#xff0c;如果是NVME接口的固态硬盘&#xff0c…

【redis】string应用场景:缓存功能和计数功能

文章目录 缓存功能实现思路存在的问题伪代码实现 记数功能实现思路统计伪代码实现 缓存功能 实现思路 整体的思路&#xff1a; 应用服务器访问数据的时候&#xff0c;先查询 Redis 如果 Redis 上数据存在了&#xff0c;就直接从 Redis 读取数据交给应用服务器&#xff0c;不继…

vue el-select 省市区三级联动 vue + element-ui使用第三方插件实现省市区三级联动

vue el-select 省市区三级联动 vue使用第三方插件实现省市区三级联动 网上找了好多教程,都是使用el-cascader级联选择器的省市区选择器,但是几乎没有三个单独的el-select的进行关联的三级省市联动组件效果 第一步:先安装省市区element-ui的插件 npm install element-china-a…

数学建模:MATLAB强化学习

一、强化学习简述 强化学习是一种通过与环境交互&#xff0c;学习状态到行为的映射关系&#xff0c;以获得最大积累期望回报的方法。包含环境&#xff0c;动作和奖励三部分&#xff0c;本质是智能体通过与环境的交互&#xff0c;使得其作出的动作所得到的决策得到的总的奖励达…

从0开始的操作系统手搓教程45——实现exec

目录 建立抽象 实现加载 实现sys_execv &#xff01;&#xff01;&#xff01;提示&#xff1a;因为实现问题没有测试。所以更像是笔记&#xff01; exec 函数的作用是用新的可执行文件替换当前进程的程序体。具体来说&#xff0c;exec 会将当前正在运行的用户进程的进程体&…

STM32之硬件SPI

SPI1和SPI2挂载的总线不一样&#xff0c;SPI1的时钟频率的比SPI2的大一倍。 核心部分是移位寄存器&#xff0c;数据一位一位的移到MOSI,同理&#xff0c;移位寄存器也一位一位的从MISO接收数据&#xff0c;LSBFIRST控制位控制高位先行还是低位先行。移位寄存器左边交叉箭头是ST…

【网络协议安全】任务10:三层交换机配置

CSDN 原创主页&#xff1a;不羁https://blog.csdn.net/2303_76492156?typeblog三层交换机是指在OSI&#xff08;开放系统互连&#xff09;模型中的第三层网络层提供路由功能的交换机。它不仅具备二层交换机的交换功能&#xff0c;还能实现路由功能&#xff0c;提供更为灵活的网…

依托大数据实验室建设,培育创新人才:数据科学与大数据技术专业人才培养实践

近年来&#xff0c;得益于全球大数据产业政策扶持与数字经济蓬勃发展&#xff0c;大数据市场呈现迅猛增长态势。国家层面相继出台《“数据要素”三年行动计划&#xff08;2024—2026年&#xff09;》《数字中国建设整体布局规划》等政策&#xff0c;旨在激发产业创新活力&#…

如何使用 CSS 实现黑色遮罩效果

最近在工作中遇见了一个需求&#xff0c;鼠标经过盒子出现黑色遮罩&#xff0c;遮罩中有相关的编辑按钮&#xff0c;点击以后&#xff0c;进行图片上传并且展示&#xff0c;由于当时没有思路&#xff0c;思考了好久&#xff0c;所以在完成开发后进行总结&#xff0c;使用的技术…