news 2026/5/26 6:11:01

【算法分析与设计】第6篇:动态规划的原理:最优子结构与重叠子问题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【算法分析与设计】第6篇:动态规划的原理:最优子结构与重叠子问题

上一篇中,Kadane算法用O(n)的时间干净利落地解决了最大子数组问题。回顾它的核心递推式dp[i]=max(dp[i-1]+A[i], A[i]),我们会发现一个更深层的结构:原问题的最优解,可以通过子问题的最优解构造出来。这正是动态规划的理论基石。

动态规划不是某一种具体的算法,而是一类求解最优化的数学方法。它的英文名“Dynamic Programming”中,“Programming”取的是“规划”而非“编程”的语义,源于上世纪五十年代Bellman在兰德公司从事运筹学研究时的命名。要准确使用这套方法,必须首先理解使它成立的两个前提条件。


一、最优子结构

定义:如果一个问题的最优解包含其子问题的最优解,则称该问题具有最优子结构性质。

这个定义值得逐字拆解。它不是说“子问题的解可以组合成原问题的解”——这是分治的前提。它说的是:原问题的最优解,必然由子问题的最优解构成。因此,在寻找全局最优时,我们无需考虑子问题的次优解,因为它们不可能参与构造全局最优。

检验最优子结构的一个通用方法是“剪贴法”:假设原问题的最优解中某个子问题的解不是最优的,那么将该子问题的解替换为更优的那个,理应得到一个更优的原问题解。若这个替换与前提矛盾,则最优子结构成立。

不是所有问题都具备最优子结构。寻找图中的最长简单路径就不具备这一性质——最长路径的子路径未必是子图的最长路径,因为最长路径的局部最优与全局最优之间存在冲突。在动态规划建模前,这一步的检验绝非可有可无。


二、重叠子问题

定义:若递归算法在求解过程中反复求解相同的子问题,而非不断产生全新的子问题,则称该问题具有重叠子问题性质。

分治算法中,子问题通常彼此独立,每个子问题只被求解一次。动态规划恰恰相反:同一个子问题可能从不同的父问题出发被多次遇到。若不刻意避免,递归树中会出现指数规模的重复计算。

斐波那契数列的递归求解是重叠子问题的经典反面教材:F(n)=F(n-1)+F(n-2),没有存储中间结果的朴素递归将导致重复计算呈指数增长。动态规划的精髓,正是通过有组织的记忆化,将每种子问题只计算一次,将指数时间压缩到多项式。


三、无后效性与状态设计

与最优子结构紧密相关的还有一个关键概念——无后效性:子问题的解一旦确定,就不再受后续更大问题决策的影响,即“未来不改变过去”。

这一性质直接指导着状态定义这个建模核心环节。状态的定义必须足够丰富,使得从该状态出发的未来决策,只依赖当前状态的信息,而不需要回溯已走过的路径。倘若状态遗漏了必要的上下文,未来决策就无法正确做出;倘若包含了冗余信息,状态空间将不必要地膨胀。状态定义的精确与简约,是动态规划建模中最考验功力的地方。


四、两种实现范式:自顶向下与自底向上

动态规划有两种对偶的实现策略。

自顶向下(备忘录法):保持递归调用的结构,但用一个数组或哈希表缓存每个子问题的解。每次进入递归前先查表,若已有结果则直接返回,否则计算并存储。优点是保留了原问题的自然递归结构,易于编写和理解,仅计算实际用到的子问题。

自底向上(表格法):显式定义子问题的求解顺序,从最小规模的子问题开始,逐步填充表格,直至得到原问题的解。优点是不需要递归调用的栈开销,求解顺序明确,便于分析复杂度。

两种实现在时间复杂度上通常一致,选择取决于问题的自然结构和个人偏好。但自底向上的方法对确定求解顺序的要求更高,在子问题依赖关系比较复杂时,需要额外进行拓扑排序。


五、实例展开:矩阵链乘法

给定n个矩阵的序列A₁,A₂,...,Aₙ,目标是找到一种加括号的方式,使得计算连乘积所需的标量乘法次数最少。矩阵乘法满足结合律但不同的括号方案开销差异巨大——一个10×100的矩阵乘100×5的矩阵需5000次乘法,再乘5×50需2500次,总计7500;但若先算后两个,开销截然不同。

状态定义:令m[i][j]为计算Aᵢ...Aⱼ所需的最小标量乘法次数。设矩阵Aᵢ的维度为pᵢ₋₁×pᵢ。

最优子结构:在Aᵢ...Aⱼ的最优加括号方案中,必然存在一个最外层划分点k(i≤k<j),使得先算左侧Aᵢ...Aₖ,再算右侧Aₖ₊₁...Aⱼ,最后将两个结果相乘。两侧子链的计算必须自身最优——这正是最优子结构的体现。由此导出状态转移方程:

m[i][j]=min⁡i≤k<j{m[i][k]+m[k+1][j]+pi−1⋅pk⋅pj}m[i][j]=i≤k<jmin​{m[i][k]+m[k+1][j]+pi−1​⋅pk​⋅pj​}

求解顺序:矩阵链长度由2增长到n,对每个长度枚举起止位置和划分点。三重循环,时间复杂度Θ(n³),空间Θ(n²)。

这个结构是动态规划的经典范式:定义状态、找出转移方程、确定填表顺序。矩阵链乘法看似是纯粹的数学问题,但在编译器设计中的寄存器分配、图像处理中的色彩空间转换等场景都有其影子。

从下一篇开始,我们将进入动态规划最具代表性的实例——01背包问题,以及它如何通过精巧的空间优化,将一个看似必须二维填表的过程压缩到一维滚动数组。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/26 6:11:00

超越调参:用Nelder-Mead算法为你的Scikit-learn模型自动寻找最佳超参数

超越调参&#xff1a;用Nelder-Mead算法为你的Scikit-learn模型自动寻找最佳超参数在机器学习项目中&#xff0c;超参数优化往往是决定模型性能的关键环节。传统方法如网格搜索和随机搜索虽然简单易用&#xff0c;但在高维参数空间中效率低下&#xff1b;而贝叶斯优化等高级方法…

作者头像 李华
网站建设 2026/5/26 6:09:00

本地大模型推理实战:Llama 108B部署、GGUF格式风险与Ollama安全加固

1. 项目概述&#xff1a;一次本地大模型推理的深度实践最近在折腾本地大模型推理&#xff0c;特别是那些动辄上百亿参数的“巨无霸”&#xff0c;比如传闻中的Llama 4 108B。这不仅仅是技术上的挑战&#xff0c;更像是一场对个人硬件、软件栈和耐心的综合考验。与此同时&#x…

作者头像 李华
网站建设 2026/5/26 6:08:06

word中如何设置多级编号,只要两步搞定

最近写文档比较多&#xff0c;总是对设置多级编号有些头疼&#xff0c;找帮助也没有得到更好的信息。今天无意中发现一个小窍门&#xff0c;跟大家分享一下。首先&#xff0c;设置第一个多级编号&#xff0c;这个相对比较简单&#xff0c;可以写3个标题&#xff08;我的习惯&am…

作者头像 李华
网站建设 2026/5/26 6:06:32

pad.ws:白板与代码编辑器合二为一的创新工具,打造无缝开发体验

前言 2025年4月21日&#xff0c;Coderamp Labs团队正式发布pad.ws&#xff0c;一款将交互式白板与完整云端IDE深度融合的开源开发工具。它彻底打破了“设计用Miro、写代码用VS Code、沟通用Slack”的多工具割裂现状&#xff0c;让开发者可以在同一个浏览器窗口中完成头脑风暴、…

作者头像 李华
网站建设 2026/5/26 6:06:30

AI时代程序员只剩架构师吗?

前言 2026年&#xff0c;AI编程工具的普及已经从“可选辅助”变成了“行业标配”。91%的工程组织已采用至少一种AI编码工具&#xff0c;谷歌内部75%的新代码由AI生成&#xff0c;Meta要求部分团队55%的代码需为AI辅助生成。与此同时&#xff0c;初级程序员岗位需求同比下降52%&…

作者头像 李华
网站建设 2026/5/26 6:05:01

避开PWM重叠的坑:Simulink仿真单电阻电流重构的移相实战(附模型)

避开PWM重叠的坑&#xff1a;Simulink仿真单电阻电流重构的移相实战&#xff08;附模型&#xff09;在电机控制系统的仿真与开发中&#xff0c;单电阻电流采样技术因其成本优势和硬件简化特点&#xff0c;成为许多工程师的首选方案。然而&#xff0c;当我们将理论转化为Simulin…

作者头像 李华