矩阵链乘法 | Matrix Chain Multiplication | 动态规划 | C/C++实现

问题描述

对于给定的n个矩阵形成的矩阵链 M 1 , M 2 , M 3 , . . . , M n M_1,M_2,M_3,...,M_n M1,M2,M3,...,Mn,求计算乘积 M 1 M 2 M 3 , . . . M n M_1M_2M_3,...M_n M1M2M3,...Mn时最少次标量相乘的运算顺序,这类问题称为矩阵链乘法问题(Matrix Chain Multiplication Problem)。

请编写一个程序,当给定矩阵 M i M_i Mi的维数后,求出计算n个矩阵的乘积 M 1 , M 2 , M 3 , . . . , M n M_1,M_2,M_3,...,M_n M1,M2,M3,...,Mn时所需标量相乘运算的最少次数。

输入: 第1行输入矩阵数n。接下来n行输入矩阵 M i ( i = 1... n ) M_i(i=1...n) Mi(i=1...n)的维数r、c。其中r代表矩阵的行数,c代表矩阵的列数,r、c均为整数,用空格隔开。
输出: 输出最少次数,占1行。
限制:
1 ≤ n ≤ 100
1 ≤ r, c ≤ 100

输入示例

6
30 35
35 15
15 5
5 10
10 20
20 25

输出示例

15125

讲解

l ∗ m l*m lm的矩阵A与 m ∗ n m*n mn的矩阵B相乘后得到 l ∗ n l*n ln的矩阵C,C的各元素 c i j c_{ij} cij可由下面的式子得出。

我们不关心 c i j c_{ij} cij的具体值,只希望尽量减少计算过程中的乘法次数。

n个矩阵相乘时, M i M_i Mi p i − 1 p_{i-1} pi1 p i p_i pi列的矩阵,我们以下图所示的 ( M 1 M 2 . . . M 6 ) (M_1M_2...M_6) (M1M2...M6)为例分析。矩阵链乘法
这戏矩阵乘积有多种计算顺序。我们按习惯的从左到右的顺序计算时可以写作 ( ( ( ( ( M 1 M 2 ) M 3 ) M 4 ) M 5 ) M 6 ) (((((M_1M_2)M_3)M_4)M_5)M_6) (((((M1M2)M3)M4)M5)M6),从右到左计算时可以写作 ( M 1 ( M 2 ( M 3 ( M 4 ( M 5 M 6 ) ) ) ) ) (M_1(M_2(M_3(M_4(M_5M_6))))) (M1(M2(M3(M4(M5M6)))))。除此之外还有 ( M 1 ( M 2 ( M 3 M 4 ) ( M 5 M 6 ) ) ) (M_1(M_2(M_3M_4)(M_5M_6))) (M1(M2(M3M4)(M5M6)))等等,计算顺序多种多样。不同顺序计算结果完全相同,但乘法运算次数会有所差异。

处理矩阵链乘法问题时,如果检查所有的运算顺序那么算法复杂度将达到 O ( n ! ) O(n!) O(n!)。不过,这个问题能够分割成更小的局部问题,我们可以运用动态规划法。

首先 ( M 1 M 2 ) (M_1M_2) (M1M2)只有一种计算方法(顺序),需要 p 0 ∗ p 1 ∗ p 2 p_0*p_1*p_2 p0p1p2次乘法运算。同理, ( M 2 M 3 ) (M_2M_3) (M2M3)也只有一种计算方法,需要 p 1 ∗ p 2 ∗ p 3 p_1*p_2*p_3 p1p2p3次乘法运算。归纳后可知, ( M i M i + 1 ) (M_iM_{i+1}) (MiMi+1)只有一种计算方法,需要 p i − 1 ∗ p i ∗ p i + 1 p_{i-1}*p_i*p_{i+1} pi1pipi+1次乘法运算。

接下来求 ( M 1 M 2 M 3 ) 、 ( M 2 M 3 M 4 ) 、 . . . 、 ( M n − 2 M n − 1 M n ) (M_1M_2M_3)、(M_2M_3M_4)、...、(M_{n-2}M_{n-1}M_n) (M1M2M3)(M2M3M4)...(Mn2Mn1Mn)的最优计算方法。计算 ( M 1 M 2 M 3 ) (M_1M_2M_3) (M1M2M3)的最优计算方法时,我们要分别算出 ( M 1 ( M 2 M 3 ) ) (M_1(M_2M_3)) (M1(M2M3)) ( ( M 1 M 2 ) M 3 ) ((M_1M_2)M_3) ((M1M2)M3)的成本,取其中较小的一个作为 ( M 1 M 2 M 3 ) (M_1M_2M_3) (M1M2M3)的成本记录在表中。

( M 1 ( M 2 M 3 ) ) = ( M 1 ) + ( M 2 M 3 ) + p 0 ∗ p 1 ∗ p 3 (M_1(M_2M_3))=(M_1)+(M_2M_3)+p_0*p_1*p_3 (M1(M2M3))=(M1)+(M2M3)+p0p1p3
( ( M 1 M 2 ) M 3 ) = ( M 1 M 2 ) + ( M 3 ) + p 0 ∗ p 2 ∗ p 3 ((M_1M_2)M_3)=(M_1M_2)+(M_3)+p_0*p_2*p_3 ((M1M2)M3)=(M1M2)+(M3)+p0p2p3

这一步用到的 ( M 1 M 2 ) (M_1M_2) (M1M2) ( M 2 M 3 ) (M_2M_3) (M2M3)的成本可以直接从表中引用,不需要再进行计算。另外还要注意,当1 ≤ i ≤ n时, ( M i ) (M_i) (Mi)的成本为0。

一般情况,矩阵链乘法 ( M i M i + 1 . . . M j ) (M_iM_{i+1}...M_j) (MiMi+1...Mj)的最优解就是 ( M i M i + 1 . . . M j ) ( M k + 1 . . . M j ) (M_iM_{i+1}...M_j)(M_{k+1}...M_j) (MiMi+1...Mj)(Mk+1...Mj)的最小成本(其中i ≤ k ≤ j)。

例, ( M 1 M 2 M 3 M 4 M 5 ) ( i = 1 , j = 5 时 ) (M_1M_2M_3M_4M_5)(i=1,j=5时) (M1M2M3M4M5)(i=1,j=5)的最优解就是下列式子中的最小值。

( M 1 ) ( M 2 M 3 M 4 M 5 ) = ( M 1 ) + ( M 2 M 3 M 4 M 5 ) + p 0 ∗ p 1 ∗ p 5 ( k = 1 时 ) (M_1)(M_2M_3M_4M_5)=(M_1)+(M_2M_3M_4M_5)+p_0*p_1*p_5(k=1时) (M1)(M2M3M4M5)=(M1)+(M2M3M4M5)+p0p1p5(k=1)
( M 1 M 2 ) ( M 3 M 4 M 5 ) = ( M 1 M 2 ) + ( M 3 M 4 M 5 ) + p 0 ∗ p 2 ∗ p 5 ( k = 2 时 ) (M_1M_2)(M_3M_4M_5)=(M_1M_2)+(M_3M_4M_5)+p_0*p_2*p_5(k=2时) (M1M2)(M3M4M5)=(M1M2)+(M3M4M5)+p0p2p5(k=2)
( M 1 M 2 M 3 ) ( M 4 M 5 ) = ( M 1 M 2 M 3 ) + ( M 4 M 5 ) + p 0 ∗ p 3 ∗ p 5 ( k = 3 时 ) (M_1M_2M_3)(M_4M_5)=(M_1M_2M_3)+(M_4M_5)+p_0*p_3*p_5(k=3时) (M1M2M3)(M4M5)=(M1M2M3)+(M4M5)+p0p3p5(k=3)
( M 1 M 2 M 3 M 4 ) ( M 5 ) = ( M 1 M 2 M 3 M 4 ) + ( M 5 ) + p 0 ∗ p 4 ∗ p 5 ( k = 4 时 ) (M_1M_2M_3M_4)(M_5)=(M_1M_2M_3M_4)+(M_5)+p_0*p_4*p_5(k=4时) (M1M2M3M4)(M5)=(M1M2M3M4)+(M5)+p0p4p5(k=4)

现在来看看这个算法的具体实现。先准备下述变量

m[n+1][n+1]:该二维数组中,m[i][j]表示计算 ( M i M i + 1 . . . M j ) (M_iM_{i+1}...M_j) (MiMi+1...Mj)时所需乘法运算的最小次数。
p[n+1]:该一维数组用于存储矩阵的行列数,其中M_i是p[i-1] * p[i]的矩阵。

利用上述变量,我们可以通过下式求出m[i][j]。

m [ i ] [ j ] = { 0 i f i = j m i n i ≤ k &lt; j ( m [ i ] [ k ] + m [ k + 1 ] [ j ] + p [ i − 1 ] ∗ p [ k ] ∗ p [ j ] i f i &lt; j m[i][j]= \begin{cases} 0 &amp; if&amp;i=j \\ min_{i≤k&lt;j}(m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j] &amp; if &amp; i&lt;j \end{cases} m[i][j]={0minik<j(m[i][k]+m[k+1][j]+p[i1]p[k]p[j]ififi=ji<j

算法实现方法如下。

matrixChainMultiplication()
	for i = 1 to n
		m[i][i] = 0

	for l = 2 to n
		for i = 1 to n - l + 1
			j = i + l - 1
			m[i][j] = INFTY
			for k = i to j - 1
				m[i][j] = min(m[i][j], m[i][k] + m[k+1][j] + p[i-1] * p[k] * p[j])

AC代码如下

#include<iostream>
#include<algorithm>
using namespace std;

static const int N = 100;

int main(){
	int n, p[N + 1], m[N + 1][N + 1];
	cin>>n;
	for(int i = 1; i <= n; i++){
		cin>>p[i - 1]>>p[i];
	}
	
	for(int i = i; i <= n; i++) m[i][i] = 0;
	for(int l = 2; l <= n; l++){
		for(int i = 1; i <= n - l + 1; i++){
			int j = i + l - 1;
			m[i][j] = (1 << 21);
			for(int k = i; k <= j - 1; k++){
				m[i][j] = min(m[i][j], m[i][k] + m[k + 1][j] + p[i - 1] * p[k] *p[j]);
			}
		}
	}
	
	cout<<m[1][n]<<endl;
	
	return 0;
} 
  • 9
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值