如果看过我上一篇文章(动态规划——最长公共子序列)的同学,相信已经对动态规划的思想有了一定的了解和认识,即把之前求解得出的子问题存储起来,以便后续求解大问题直接使用。

本章要讲解的问题是矩阵链相乘。因为要照顾一些小白同学和方便撰写文章,我会写得比较详细,如果有基础、想看ac代码的同学可直接跳到后面的代码段。矩阵链相乘,顾名思义就是多个矩阵相乘,那为什么同样的矩阵相乘得出来的乘法次数会不一样呢?下面来看一个例子。

三个二维矩阵M1、M2、M3,分别是2*10,10*2,2*10。如果(M1*M2)*M3,那么要进行的乘法次数是2*10*2+2*2*10=80;如果M1*(M2*M3),那么要进行的乘法次数是10*2*10+2*10*10=400(看不懂乘法次数算式的同学可以先去复习一下线性代数的知识)。所以,矩阵链相乘的相乘次数会根据矩阵间的不同相乘顺序而有所不同。看懂原理之后,那我们来做一个题。


矩阵链乘 (转自PTA)

用动态规划法确定n个矩阵链乘M1​M2​...Mn​运算量最小(即数量乘法次数最少)的计算次序.。

输入格式:

输入的第一行包含一个整数n(1≤n≤20),接下来一行是n+1个数依次是n个矩阵的行数以及最后一个矩阵的列数。注意,根据矩阵乘法定义,两个矩阵相乘Mi​Mi+1​意味着后一矩阵Mi+1​的行数与前一矩阵Mi​的列数相同。

输出格式:

在一行内输出这n个矩阵链乘时数量乘法的最少次数,以及一种对应的最优计算次序。最优计算次序采用对M1​M2​...Mn​加括号的形式输出,一共n−1对括号(参见输出样例)。
如果有多种最优计算次序,则取每层括号尽量靠前的那种次序(即每层括号划分的两个矩阵子序列中前面子序列尽可能短的那种)。例如,如果M1​M2​M3​M4​M5​的最优计算次序的最外层括号有两种方式:(M1​M2​M3​)(M4​M5​)和(M1​M2​)(M3​M4​M5​), 则最外层括号取后一种,因为其括号更靠前。

输入样例:

5
5 10 4 6 10 2

输出样例:

348 (M1(M2(M3(M4M5))))

我们先来整理一下思路。假如只有一个矩阵,那乘法次数自然为0;当有两个矩阵时,次数为矩阵行列直接相乘可得;当有三个矩阵时,就有两种相乘方式,即(M1*M2)*M3和M1*(M2*M3);以此类推,放到二维数组中可以如下表示:

m[i, j]表示的是矩阵i到矩阵j链相乘所需要的最少乘法次数。d代表的是有d个矩阵相乘,可知d=1这一斜线上的m[i, j]都等于0;d=2时,斜线上的m[i, j]等于两个矩阵行列直接相乘;d=3时,就有两种选择,例如m[1,3]=min{m[1, 2]+m[3, 3]+a[1]*a[3]*a[4], m[1, 1]+m[2, 3]+a[1]*a[2]*a[4]} //数组a存储矩阵的行和列。大家可以发现,求m[1, 3]的时候,需要用到的m[1, 2]、m[3, 3]、m[1, 1]、m[2, 3]都是前面已经求得的,直接拿来运算就可以了,这就是本题动态规划的思路。后面的原理也是一样的,就不赘述。

所以,我们可以得出一个求m[i, j]的公式:m[i, j]=min{ m[i, j],  m[i, k]+m[k+1, j]+a[i]*a[k+1]*a[j+1] }。大家可以把k理解为中转站,比如汽车高速路上行驶,如果跑长途的话,一般都会在某个服务站下车休息一下,那我们要做的就是找出在哪个服务站下车休息,会使整个的行驶时间最短。

for(int d=2;d<=n;d++)//d个矩阵相乘
{for(int i=1;i<=n-d+1;i++)//斜着到第i个 {int j=i+d-1;m[i][j]=inf;for(int k=i;k<=i+d-2;k++){int temp=m[i][k]+m[k+1][j]+a[i]*a[k+1]*a[j+1];if(temp<m[i][j]){m[i][j]=temp;s[i][j]=k;}} }
} 

(1)d=1时,为一个矩阵的情况,m[i, i]=0,所以不需要求解。

(2)i是所在斜线中的第i个(大家在看代码的时候可以结合上面的图片一起看,方便理解),j是受到i和d的影响,并且都是由固定的表达式求得,不需要重新定义。

(3)k是从i到j之间的中转站(大家注意k的范围,最大范围是j-1), 逐一遍历,如果第k个中转站得出来的结果比之前的小,则把m[i, j]更新。

(4)二维数组s是记录i到j之间的哪个中转站可使m[i, j]最小,便于后续打印。

打印矩阵链相乘的最优计算次序

void m_print(int i, int j)
{if(i==j){printf("M%d", i);}else{printf("(");m_print(i, s[i][j]);m_print(s[i][j]+1, j);printf(")");}
}

先来看else代码段,用递归输出,当i != j时,说明i到j还有中转站,即s[i][j],一直递归;直到i==j时,说明是同个矩阵,没有中转站了,可以打印输出。

本题的ac代码如下:

#include<iostream>
#define inf 100000001
using namespace std;
int a[21]={0};//存储矩阵的行和列
int m[21][21]={0};//存储i到j的最少计算次数
int s[21][21]={0};//存储i到j的中转站k
void m_print(int i, int j)
{if(i==j){printf("M%d", i);}else{printf("(");m_print(i, s[i][j]);m_print(s[i][j]+1, j);printf(")");}
}
int main()
{int n=0;scanf("%d",&n);for(int i=1;i<=n+1;i++){scanf("%d",&a[i]);}for(int d=2;d<=n;d++)//d个矩阵相乘 {for(int i=1;i<=n-d+1;i++)//斜着到第i个 {int j=i+d-1;m[i][j]=inf;for(int k=i;k<=i+d-2;k++){int temp=m[i][k]+m[k+1][j]+a[i]*a[k+1]*a[j+1];if(temp<m[i][j]){m[i][j]=temp;s[i][j]=k;}} }} printf("%d ",m[1][n]);m_print(1, n);return 0;
}

希望能帮助到大家!

动态规划——矩阵链相乘相关推荐

  1. Java动态规划---矩阵链相乘的最小计算代价

    参考书籍:算法导论第三版. 采用自底向上的递归模式来求解. * 动态规划在矩阵链相乘的应用,目的求出最小的计算代价,即矩阵的计算顺序,用加小括号表示. * 主要的计算思想是递归,而且是带备忘录的递归, ...

  2. 以空间换时间——动态规划算法及其应用:矩阵链相乘

    动态规划算法是5大算法基础中最重要的一个,它专门用来解决平面世界下的应用,即会多次使用二维数组. 当然动态规划算法是空间换时间的算法,也就是说:我们可以利用空间资源来使某算法问题的时间复杂度降到最低. ...

  3. 【动态规划】矩阵链相乘 (ssl 1596)/能量项链 (ssl 2006)

    矩阵链相乘{\color{Cyan} 矩阵链相乘 }矩阵链相乘 Description Input n表示矩阵的个数(<=100) n+1个数,表示矩阵(<=100) Output 最小的 ...

  4. 矩阵链相乘(动态规划法)

    矩阵链乘法是耳熟能详的问题了,有很多矩阵,排列成矩阵链,矩阵链的要求是相邻的两个是可以相乘的,可以相乘是有条件的,两个矩阵能够相乘的条件就是行.列之间是有关系的,两个矩阵如果能够相乘,就是前面矩阵的列 ...

  5. 矩阵相乘的strassen算法_矩阵乘法的Strassen算法+动态规划算法(矩阵链相乘和硬币问题)...

    矩阵乘法的Strassen 这个算法就是在矩阵乘法中采用分治法,能够有效的提高算法的效率. 先来看看咱们在高等代数中学的普通矩阵的乘法 两个矩阵相乘 上边这种普通求解方法的复杂度为: O(n3) 也称 ...

  6. 7-1 矩阵链相乘问题 (20 分)(思路+详解+题目解析) 动态规划做法

    一:题目: 输入样例: 在这里给出一组输入.例如: 5 30 35 15 5 10 20 输出样例: 在这里给出相应的输出.例如: 11875 二:基本解析 1.基本的动态规划知识: 1):求解过程是 ...

  7. 【动态规划】矩阵链乘法

    矩阵链乘法    求解矩阵链相乘问题时动态规划算法的另一个例子.给定一个n个矩阵的序列(矩阵链)<A1,A2,...,An>,我们希望计算它们的乘积  A1A2...An    为了计算表 ...

  8. 动态规划典型题之——矩阵链乘法

    动态规划是算法分析与设计中一种重要的算法.其核心思想是将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法. 文章目录 一.矩阵链相乘问题 二.求解 1.构建备忘录 2.回溯 结果展示 一.矩 ...

  9. 【动态规划】区间DP - 最优矩阵链乘(另附POJ1651Multiplication Puzzle)

    最优矩阵链乘(动态规划) 一个n∗mn*mn∗m的矩阵由 nnn 行 mmm 列共 n∗mn*mn∗m 排列而成.两个矩阵A和B可以相乘当且仅当A的列数等于B的行数.一个nm的矩阵乘mp的矩阵,运算量 ...

最新文章

  1. Wiki为什么会流行
  2. UITabelView使用流程
  3. Cloud Toolkit 部署应用到 EDAS Kubernetes 集群
  4. JAVA内存的可见性
  5. Linux 命令(47)—— file 命令
  6. 暴露了自己的无知不是问题,问题是还坚决不改
  7. 在浏览器中输入url地址 - 显示主页的过程
  8. 解析GitHub首页3D动画
  9. JS五彩连珠小游戏(Canvas绘制)
  10. 惠斯通电桥称重传感器检测原理
  11. 单表置换加密matlab,单表置换密码
  12. 搭建网站服务器必须开443端口,HTTPS端口必须一定要443吗?
  13. 木讷的程序员需要知道的事情 (四)
  14. 可长期免费使用的国产PLC录波软件(数据采集软件)PLC-Recorder V2.0版新功能
  15. classes是什么意思怎么读_class是什么意思_class的翻译_音标_读音_用法_例句_爱词霸在线词典...
  16. 经典Java开发教程 腾讯+字节+阿里面经真题汇总,斩获offer
  17. RS232和RS485概念与区别
  18. 常用crc查表法_查表法计算CRC
  19. 从源码角度上探索AdapterViewFlipper怎么实现广告栏的垂直自动滚动
  20. 主攻文推荐攻守都有系统_坚守最后一道防线-第五十五章 攻守转换在线阅读-顶点小说...

热门文章

  1. PP-LiteSeg: A Superior Real-Time Semantic Segmentation Model-2022.4.6
  2. 音频内容理解的关键技术
  3. 做梦并且坚信自己能遇到一个绅士般的BF
  4. 我把近视从600度直降到0度 作者:灵魂旗舰 谈谈治疗近视的雾视疗法
  5. js字符串拼接 ·${}·
  6. java里remark是什么意思_remark是什么意思_remark在线翻译_英语_读音_用法_例句_海词词典...
  7. 电子手轮 (位置跟随,X轴或Y轴) 200smart、威纶通触摸屏
  8. jQuery 设置设置
  9. “医疗大数据”的三重困境
  10. MATLAB | 那些你不得不知道的MATLAB小技巧(二)