[题解]矩阵链乘法

48 篇文章 4 订阅

题目

题目描述

给定矩 n n n的矩阵的维数后,丘处计算 n n n个矩阵的成绩 M 1 M 2 M 3 . . . M n M_1M_2M_3...M_n M1M2M3...Mn时所需标量相乘运算的最少次数。

输入格式

1 1 1行输入矩阵数 n ( 1 ≤ n ≤ 100 ) n(1 \leq n \leq 100) n1n100
接下来 n n n行,每行给定两个数 r i , c i ( 1 ≤ r , c ≤ 1000 ) r_i,c_i(1 \leq r,c \leq 1000) ri,ci1rc1000,分别对应矩阵 M i M_i Mi的行数和列数。

输出格式

输出一个整数,代表最少次数。

题解

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 1010;
int f[N][N];
int P[N];
int main(){
    int n;
    scanf("%d",&n);
    for(int i = 0;i < n;i++) scanf("%d%d",&P[i],&P[i + 1]);
    
    for(int len = 2;len <= n;len++)
        for(int i = 1;i + len - 1 <= n;i++){
            int j = i + len - 1;
            f[i][j] = 2e9;
            for(int k = i;k < j;k++)
                f[i][j] = min(f[i][j],f[i][k] + f[k + 1][j] + P[i - 1] * P[k] * P[j]);
        }
    printf("%d\n",f[1][n]);
    return 0;
}

这道题是可以使用区间dp求解。

在解题之前,需要先对矩阵乘法有充分的认识。
点这里了解矩阵乘法

特别注意矩阵乘法的两个特性:

  • 当矩阵A的列数等于矩阵B的行数时,A与B可以相乘。
  • 矩阵乘法一般不满足交换律。
    • A可以乘以B,B不一定就能乘以A
    • 但A可以乘以B时, A × B A×B A×B 也不一定等于 B × A B × A B×A

因为这两个特性,所以我们不能使用交换律来减少标量计算次数。

但,矩阵还有另外的一个特性:

  • 矩阵满足结合律。
    因此我们可以考虑通过借助结合律来减少计算次数。
    在这里插入图片描述
    如图所示,矩阵乘法存在多种可以结合方式,但可以肯定的是,其中必定存在一种最有的结合方式。

如果使用朴素的思路,那便是把结合方式都枚举一遍,找出其中最优的一个即可。

然而这种解法虽然思路简单,但时间复杂度为 O ( n ! ) O(n!) O(n!),在数据规模较大时运算速度不尽人意。

枚举所有方案会超时,但不枚举所以方案又难以找出最小值,这该怎么办呢?

我们先来研究一个问题,为什么我们枚举所有方案会超时呢?
请看下图:
在这里插入图片描述
我们发现,如果使用朴素的做法进行一一枚举,那么势必会有很多方案存在重复计算某一个区间的最优解的情况。如图中的方案,都会计算一遍 M 1 ∗ M 2 M_1*M_2 M1M2的最优解。而像是第4、5个方案,类似的也会把 ( M 1 ∗ M 2 ) ∗ M 3 (M_1 * M_2) * M3 (M1M2)M3进行重复计算。

如果我们将这些结果在一次计算后保存起来,在之后需要时取出该数值,不就能免去很多的重复的计算内容,优化运算速度了吗?

因此,我们可以考虑使用一个二维数组,存储相关的值。

f [ i ] [ j ] ( 0 < i < j ≤ n ) f[i][j](0 < i < j \leq n) f[i][j]0<i<jn:表示从第 i i i个矩阵到第 j j j个矩阵的标量乘法的最小值。

那么 f [ i ] [ j ] f[i][j] f[i][j]的值怎么计算出来呢?

我们先来想想看, f [ 1 ] [ 3 ] f[1][3] f[1][3]的值该怎么计算呢?

在这里插入图片描述
如图所示,因为 f [ 1 ] [ 3 ] f[1][3] f[1][3]表示的是 M 1 ∗ M 2 ∗ M 3 M_1 * M_2 * M_3 M1M2M3的标量最小值,所以存在两种可能的方案,我们只需要比较这两种方案各自的计算量即可找到最小值。

对于方案一: 计 算 量 = M 1 ∗ M 2 的 计 算 量 + ( M 1 M 2 ) ∗ M 3 的 计 算 量 计算量 = M_1 * M_2 的计算量 + (M_1M_2) * M_3的计算量 =M1M2+(M1M2)M3
对于方案二: 计 算 量 = M 2 ∗ M 3 的 计 算 量 + M 1 ∗ ( M 2 M 3 ) 的 计 算 量 计算量 = M_2 * M_3 的计算量 + M_1 * (M_2M_3)的计算量 =M2M3+M1(M2M3)

并且,根据 f [ i ] [ j ] f[i][j] f[i][j]的状态表示,我们可以得出,
M 1 ∗ M 2 的 计 算 量 = f [ 1 ] [ 2 ] M_1 * M_2的计算量 = f[1][2] M1M2=f[1][2]
M 2 ∗ M 3 的 计 算 量 = f [ 2 ] [ 3 ] M_2 * M_3的计算量 = f[2][3] M2M3=f[2][3]

因此,

对于方案一: 计 算 量 = f [ 1 ] [ 2 ] + ( M 1 M 2 ) ∗ M 3 的 计 算 量 计算量 = f[1][2] + (M_1M_2) * M_3的计算量 =f[1][2]+(M1M2)M3
对于方案二: 计 算 量 = f [ 2 ] [ 3 ] + M 1 ∗ ( M 2 M 3 ) 的 计 算 量 计算量 = f[2][3] + M_1 * (M_2M_3)的计算量 =f[2][3]+M1(M2M3)

又由矩阵乘法的特性,我们可以得出:
( M 1 M 2 ) ∗ M 3 的 计 算 量 = r 1 ∗ r 3 ∗ c 3 = r 1 ∗ c 2 ∗ c 3 (M_1M_2) * M_3的计算量 = r_1 * r_3 * c_3 = r_1 * c_2 * c3 (M1M2)M3=r1r3c3=r1c2c3
M 1 ∗ ( M 2 M 3 ) 的 计 算 量 = r 1 ∗ r 2 ∗ c 3 = r 1 ∗ c 1 ∗ c 3 M_1 * (M_2M_3)的计算量 = r_1 * r_2 * c_3 = r_1 * c_1 * c3 M1(M2M3)=r1r2c3=r1c1c3

因此,
对于方案一: 计 算 量 = f [ 1 ] [ 2 ] + r 1 ∗ r 3 ∗ c 3 = f [ 1 ] [ 2 ] + r 1 ∗ c 2 ∗ c 3 计算量 = f[1][2] + r_1 * r_3 * c_3 =f[1][2] + r_1 * c_2 * c3 =f[1][2]+r1r3c3=f[1][2]+r1c2c3
对于方案二: 计 算 量 = f [ 2 ] [ 3 ] + r 1 ∗ r 2 ∗ c 3 = f [ 2 ] [ 3 ] + r 1 ∗ c 1 ∗ c 3 计算量 = f[2][3] + r_1 * r_2 * c_3 = f[2][3] +r_1 * c_1 * c3 =f[2][3]+r1r2c3=f[2][3]+r1c1c3

综合如上规律,我们可以得出:
f [ i ] [ j ] = { m i n ( f [ i ] [ k ] + f [ k + 1 ] [ j ] + P [ i − 1 ] ∗ P [ k ] ∗ P [ j ] , i < j 0 , i = j f[i][j] = \left\{\begin{matrix} min(f[i][k] + f[k + 1][j] + P[i - 1] * P[k] * P[j],i < j\\ 0,i = j \end{matrix}\right. f[i][j]={min(f[i][k]+f[k+1][j]+P[i1]P[k]P[j],i<j0,i=j
其中, i ≤ k < j i \leq k < j ik<j,P[i - 1]、P[i]为 M i M_i Mi的行数和列数。

那么代码似乎就"呼之欲出"了!

但还有一点需要特别注意,那便是计算顺序。不难发现,我们在计算 f [ 1 ] [ 3 ] f[1][3] f[1][3]的值的时候,需要提前知道 f [ 1 ] [ 2 ] f[1][2] f[1][2] f [ 2 ] [ 3 ] f[2][3] f[2][3]最终最优取值。同样,我们在计算一个更大的区间的最优值时,要求其包含的所有小区间的最优值已知。

因此我们需要特别注意计算顺序应该是从小区间向大区间开始计算。
如代码所示:

    for(int len = 2;len <= n;len++)
        for(int i = 1;i + len - 1 <= n;i++){
            int j = i + len - 1;
            f[i][j] = 2e9;
            for(int k = i;k < j;k++)
                f[i][j] = min(f[i][j],f[i][k] + f[k + 1][j] + P[i - 1] * P[k] * P[j]);
        }

循环的最外层是对区间长度的枚举,区间长度从2开始枚举。
内层循环计算每一个长度为 l e n len len的区间的最优值。
最终, f [ 1 ] [ n ] f[1][n] f[1][n]就是我们想要的结果了。


原创不易,感谢支持!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wingaso

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值