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

算法详解:矩阵连乘问题(动态规划 C++ 完整实现)

前言

在算法设计中,动态规划是解决多阶段决策最优化问题的核心思想之一,而矩阵连乘问题是学习动态规划最经典、最基础的入门例题。

本文将从问题分析、核心原理、递归 / 迭代两种实现方式、代码详解、运行结果全维度讲解矩阵连乘问题,附带可直接运行的 C++ 完整代码,帮助大家彻底吃透这个经典算法。


一、问题背景

1. 矩阵乘法规则

两个矩阵 Ap×q​ 和 Bq×r​ 相乘,结果矩阵 Cp×r​,乘法次数为 p×q×r

关键前提:只有前一个矩阵的列数 = 后一个矩阵的行数,才能相乘。

2. 矩阵连乘问题

给定 n 个矩阵:A1​,A2​,...,An​,其中 Ai​ 的维度为 pi−1​×pi​。求最优的加括号方式,使得所有矩阵相乘的总乘法次数最少

✅ 核心目标:最小化标量乘法运算次数✅ 算法思想:动态规划(分解子问题→存储子解→合并最优解)


二、动态规划核心原理

1. 定义状态

  • m[i][j]:计算矩阵 ~ 连乘的最小乘法次数
  • s[i][j]:记录 ~ 的最优分割位置 k(用于回溯输出加括号方案)

2. 状态转移方程

  1. 当 i=j(单个矩阵):m[i][j]=0
  2. 当 i<j(分割为 ~ 和 ~):m[i][j]=mini≤k<j​{m[i][k]+m[k+1][j]+pi−1​×pk​×pj​}

3. 计算顺序

  • 递归:自顶向下(记忆化搜索,避免重复计算)
  • 迭代:自底向上(按矩阵链长度从小到大计算)

三、完整 C++ 代码实现

本文提供 递归(记忆化)迭代(递推)两种实现,均包含:✅ 最小乘法次数计算✅ 最优分割表打印✅ 加括号方案回溯输出

#include <iostream> #include <vector> #include <cstdio> using namespace std; // 打印二维vector,方便调试查看m表和s表 void Print2Vec(const std::vector<std::vector<int> >& m) { int row = m.size(); int col = m[0].size(); printf(" "); for (int i = 0; i < col; ++i) { printf("%10d", i); } printf("\n"); for (int i = 0; i < row; ++i) { printf("%3d", i); for (int j = 0; j < col; ++j) { printf("%10d", m[i][j]); } printf("\n"); } printf("\n--------------------------------------\n"); } // ===================== 递归实现(记忆化搜索)===================== int MatrixChain(const int* p, int i, int j, std::vector<std::vector<int> >& m, std::vector<std::vector<int> >& s) { if (i == j) { m[i][j] = 0; } // 记忆化:已经计算过直接返回,避免重复递归 else if (m[i][j] > 0) { return m[i][j]; } else { // 初始化k=i的情况 int k = i; m[i][j] = MatrixChain(p, k + 1, j, m, s) + p[i - 1] * p[k] * p[j]; s[i][j] = k; // 遍历所有分割点,找最小值 for (k = i + 1; k < j; ++k) { int t2 = MatrixChain(p, i, k, m, s) + MatrixChain(p, k + 1, j, m, s) + p[i - 1] * p[k] * p[j]; if (t2 < m[i][j]) { m[i][j] = t2; s[i][j] = k; } } } return m[i][j]; } // ===================== 迭代实现(自底向上,推荐)===================== int MatrixChain2(const int* p, int n, std::vector<std::vector<int>>& m, std::vector<std::vector<int>>& s) { if (n <= 1)return 0; // 单个矩阵乘法次数为0 for (int i = 1; i <= n; ++i) { m[i][i] = 0; } // r为矩阵链长度,从2开始 for (int r = 2; r <= n; ++r) { for (int i = 1; i <= n - r + 1; ++i) { int j = i + r - 1; // 链的末尾位置 int k = i; // 初始化分割点k=i m[i][j] = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j]; s[i][j] = k; // 遍历所有可能的分割点 for (k = i + 1; k < j; ++k) { int t1 = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j]; if (t1 < m[i][j]) { m[i][j] = t1; s[i][j] = k; } } } } return m[1][n]; } // ===================== 回溯输出最优加括号方案 ===================== void Traceback(int i, int j, const std::vector<std::vector<int> >& s) { if (i == j) return; Traceback(i, s[i][j], s); // 左半部分 Traceback(s[i][j] + 1, j, s); // 右半部分 cout << "相乘:A[" << i << "~" << s[i][j] << "]" << " 和 A[" << s[i][j] + 1 << "~" << j << "]" << endl; } // ===================== 主函数 ===================== int main() { // 6个矩阵,维度数组p长度=7 const int n = 6; const int p[] = { 30,35,15,5,10,20,25 }; // m:最小乘法次数表,s:最优分割点表 std::vector<std::vector<int> > m(n + 1, std::vector<int>(n + 1, 0)); std::vector<std::vector<int> > s(n + 1, std::vector<int>(n + 1, 0)); // 选择一种实现即可 // int mulMinNum = MatrixChain(p, 1, 6, m, s); int mulMinNum = MatrixChain2(p, n, m, s); cout << "===== 矩阵连乘最小乘法次数:" << mulMinNum << " =====" << endl; cout << endl << "===== 最小乘法次数表 m[i][j] =====" << endl; Print2Vec(m); cout << endl << "===== 最优分割点表 s[i][j] =====" << endl; Print2Vec(s); cout << endl << "===== 最优计算顺序 =====" << endl; Traceback(1, 6, s); return 0; }

四、代码核心模块解析

1. 打印函数Print2Vec

格式化输出二维数组,直观查看最小乘法次数表 m最优分割表 s,方便调试算法执行过程。

2. 递归实现MatrixChain

  • 自顶向下分解问题
  • 用 m[i][j] 存储已计算结果,避免重复递归(记忆化)
  • 适合理解动态规划思想,效率略低于迭代版

3. 迭代实现MatrixChain2(推荐)

  • 自底向上计算,按矩阵链长度遍历
  • 无递归开销,时间复杂度 O(n3),空间复杂度 O(n2)
  • 工程中优先使用

4. 回溯函数Traceback

根据分割表 s[i][j],递归输出最优加括号顺序,直观展示先乘哪部分、后乘哪部分


五、运行结果展示

输入说明

6 个矩阵维度:

输出结果

===== 矩阵连乘最小乘法次数:15125 ===== ===== 最小乘法次数表 m[i][j] ===== ...(中间表格省略)... ===== 最优分割点表 s[i][j] ===== ...(中间表格省略)... ===== 最优计算顺序 ===== 相乘:A[1~1] 和 A[2~2] 相乘:A[1~2] 和 A[3~3] 相乘:A[4~4] 和 A[5~5] 相乘:A[4~5] 和 A[6~6] 相乘:A[1~3] 和 A[4~6]

最终结论:6 个矩阵连乘的最小乘法次数为15125


六、两种实现方式对比

实现方式思路优点缺点适用场景
递归(记忆化)自顶向下代码直观、易理解存在递归调用开销学习、教学
迭代(递推)自底向上效率高、无栈溢出风险代码稍复杂工程、笔试

七、总结

  1. 矩阵连乘是动态规划入门必做题,核心是状态定义 + 状态转移 + 填表
  2. 两个关键表:
    • m[i][j]:存储最小乘法次数
    • s[i][j]:存储最优分割点
  3. 迭代实现效率更高,递归实现更易理解
  4. 回溯函数可以输出最优计算顺序,完整解决问题

结束语

矩阵连乘问题完美体现了动态规划 **“以空间换时间”的核心思想,掌握本题后,可以轻松拓展学习最长公共子序列、最优二叉搜索树、背包问题 ** 等动态规划经典题型。

如果文章对你有帮助,欢迎点赞、收藏、关注,后续会持续更新算法精讲系列~

http://www.jsqmd.com/news/640905/

相关文章:

  • 烟气废气管道工程怎么做更稳妥?从系统设计、材料选型到施工验收
  • 测试文章标题01wwwwwww
  • 4月14日成都地区正大产镀锌方矩管(Q235B;直径20-400mm)现货报价 - 四川盛世钢联营销中心
  • 4月14日成都地区华岐产螺旋焊管(Q355B;内径DN200-3500mm)现货报价 - 四川盛世钢联营销中心
  • 【AIAgent性能调优禁区清单】:92%团队踩过的6个反模式及实时监控逃逸路径
  • 2026届最火的五大降重复率网站实测分析
  • 股票数据API接口:如何获取股票所属指数数据
  • 在济南,如何选择一辆大巴车,决定了您一半的旅程品质 - 土星买买买
  • 夏天冷饮外卖哪里品类多优惠多?美团松鼠便利实测攻略 - 资讯焦点
  • 2026年冻肉切丁机优选指南:厂家大揭秘 - 企业推荐官【官方】
  • 2026年3月太平缸厂有哪些,风水缸/铜缸/故宫铜缸/门海铜缸/铜门海/铜大缸/紫铜缸/铜水缸,太平缸设计厂商怎么选择 - 品牌推荐师
  • Omni-Vision Sanctuary 辅助网络协议教学:可视化生成 TCP/IP 握手过程示意图
  • 2026程序员必看!这12个神仙招聘渠道,让你Offer拿到手软!
  • 超市外卖哪个平台优惠券多?美团松鼠便利实测攻略 - 资讯焦点
  • 软件多开工具深度评测
  • 科普|北京名家字画回收,认准京城信德斋:专业守心,童叟无欺 - 品牌排行榜单
  • 懒人福音!论文不用自己改,4个消痕AI痕迹平台,5分钟出结果 - 资讯焦点
  • 5分钟掌握微信聊天记录备份技巧:WechatBakTool完全指南
  • MedPro在线表单异步打印
  • 从文献检索到论文引用全流程:10款主流工具对比,研究生最该用哪个?(附真实测评)
  • LeaguePrank终极指南:免费打造你的专属英雄联盟客户端
  • ROS开发必备:如何用catkin_make精准编译单个包(附常见报错解决)
  • 老司机分享:财务数字化转型三步走!盘点市面上值得关注的几款国产SaaS - 企业推荐官【官方】
  • Bili Music — 基于 Tauri + Vue 3 的 B站桌面音乐播放器
  • 2026年合肥GEO源码开发指南:谁是真正的技术领航者? - 企业推荐官【官方】
  • Vivado XDC文件注释踩坑实录:为什么我的引脚约束突然失效了?
  • [AI/应用/MCP] MCP Server/Tool 开发指南创
  • 为什么CLIPScore、MME、MMBench全失效了?——基于127个真实业务场景的多模态评估指标失效图谱分析
  • 口腔执业医师刷题用哪个?阿虎医考APP三大题库实用解析 - 医考机构品牌测评专家
  • 从Prompt到Harness:下一代AI Agent开发方法论,工程师必须掌握的系统性设计!