题目
题目描述
给定矩 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)
n(1≤n≤100)。
接下来
n
n
n行,每行给定两个数
r
i
,
c
i
(
1
≤
r
,
c
≤
1000
)
r_i,c_i(1 \leq r,c \leq 1000)
ri,ci(1≤r,c≤1000),分别对应矩阵
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
M1∗M2的最优解。而像是第4、5个方案,类似的也会把
(
M
1
∗
M
2
)
∗
M
3
(M_1 * M_2) * M3
(M1∗M2)∗M3进行重复计算。
如果我们将这些结果在一次计算后保存起来,在之后需要时取出该数值,不就能免去很多的重复的计算内容,优化运算速度了吗?
因此,我们可以考虑使用一个二维数组,存储相关的值。
f [ i ] [ j ] ( 0 < i < j ≤ n ) f[i][j](0 < i < j \leq n) f[i][j](0<i<j≤n):表示从第 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
M1∗M2∗M3的标量最小值,所以存在两种可能的方案,我们只需要比较这两种方案各自的计算量即可找到最小值。
对于方案一:
计
算
量
=
M
1
∗
M
2
的
计
算
量
+
(
M
1
M
2
)
∗
M
3
的
计
算
量
计算量 = M_1 * M_2 的计算量 + (M_1M_2) * M_3的计算量
计算量=M1∗M2的计算量+(M1M2)∗M3的计算量
对于方案二:
计
算
量
=
M
2
∗
M
3
的
计
算
量
+
M
1
∗
(
M
2
M
3
)
的
计
算
量
计算量 = M_2 * M_3 的计算量 + M_1 * (M_2M_3)的计算量
计算量=M2∗M3的计算量+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]
M1∗M2的计算量=f[1][2]
M
2
∗
M
3
的
计
算
量
=
f
[
2
]
[
3
]
M_2 * M_3的计算量 = f[2][3]
M2∗M3的计算量=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的计算量=r1∗r3∗c3=r1∗c2∗c3
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)的计算量=r1∗r2∗c3=r1∗c1∗c3
因此,
对于方案一:
计
算
量
=
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]+r1∗r3∗c3=f[1][2]+r1∗c2∗c3
对于方案二:
计
算
量
=
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]+r1∗r2∗c3=f[2][3]+r1∗c1∗c3
综合如上规律,我们可以得出:
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[i−1]∗P[k]∗P[j],i<j0,i=j
其中,
i
≤
k
<
j
i \leq k < j
i≤k<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]就是我们想要的结果了。
原创不易,感谢支持!