直入主题,5.27学的矩阵链相乘(动态规划)
题目理解:
1.原题
要求:对A1,A2,A3......An进行矩阵的乘法(线性代数的基础知识),求通过添加括号,以达到的最小乘法次数
2. 题目理解
乘法:由于矩阵乘法的结合律,只要不调整矩阵乘法过程中的位置,就不会导致答案不同
最小乘法次数: 通过完全括号化(其实就是加括号运用矩阵乘法结合律实现),实现乘法次数的减少,由于括号化之后可以看成一个整体,也就变成了状态的转移,形成一个动态规划的求解全局最优解的题目
解题思路(动归四部曲)
0. 题目理解
1.一个矩阵乘法是A.rows乘B.columns,a行b列 x b行c列,就会进行b次的乘法,一共有a x c的乘法,所以一共是a x b x c
2. 我们还需要额外保存一下所有的原始代价,开拓一个P数组
3. 不同的顺序会导致不同的计算量,类似于A1为10x100, A2为100x5,A3为5x50
如果是((A1A2)A3),则计算量为10 x 100 x 5 + 10 x 5 x 50 = 7500
如果是(A1(A2A3)),则计算量为100 x 5 x 50 + 10 x 100 x 50 = 75000
所以当两个矩阵相乘的顺序不同,会导致整体的计算量不同,因此需要决定好最优决策,因此可以与一种最优化算法有关——动态规划
1. dp数组下角标含义
一般开设二维数组递归,在这一题这是必要的,无法压缩
memo[i][j],i代表第i个矩阵,j代表第j个矩阵,这是最简单的理解,先这样看看,这也是一般化的解,如果有特殊情况再回来看就可以啦
2. 递推公式理解
对于这道题目,我们可以知道我们状态的转移都来自于括号括在哪里,并且不同的括号的代价也不同,每一次状态转移的时候都是要求最优化的状态转移,最优化就是在i到j中的矩阵找到一个位置将我们的括号放进去,这个时候就是最优解啦
现在开始对题目递推公式的解释啦:
首先我们多引入了一个k,i <= k < j其关键含义就是我们会在k与k + 1插入一个括号,使得这个时候我们所需的乘数最小化。
状态转移时的情况:转移状态就是括号的移动,所以当括号转移的时候就是k值的移动,所以就是k的改变,而ij是不会在外面随着k改变,此时就是通过k的变化继承上一次的代价(一共有两个部分,一个部分是最优的左侧,另一个部分是最优的右侧),同时,我们也要把自己的乘法的代价算进去,我们通过0.题目理解处的公式,可以知道最优的左侧x最优的右侧,最优的最左侧的值是P(i - 1),最优的最右侧是P(j),中间则是P(k),所以最后要加上 P(i - 1)P(k)P(j)这样的一个长串代表自己的代价
小小的总结:前两个由状态转移方程得出,最后一个由0.题目理解得出
memo[i][k]代表了最优化情况下左半边的代价
memo[k + 1][j]代表了最优化情况下右半边的代价
P(i - 1)P(k)P(j)代表了最优化情况下自己的代价
3. dp数组初始化
由于当i == j时,这时候意味着最优化就是自己,那么代价当然为0,所以memo[i][i] == 0
当 i < j的时候,此时初始化因为都要取最小值,所以没取到的时候应该要选正无穷,此时才能成立,能够不断更新最小值(大部分初始化就分为这两种,一种取负无穷不断更新最大值,一种取正无穷不断更新最小值)
4. 遍历方式
由于开的是一个二维数组,此时重要的就是i,j的遍历,我们默认i是小的也就代表着i是数组维度的部分,j是大的也就是数组长度的部分,但是我们发现,memo[k + 1][j],k + 1一定是大于i的,所以如果i是正向遍历的话,此时所有的数都是正无穷,因为正无穷的代价被提前加上去了,由此得知我们的i是反向遍历
对于i和j呢,我们发现好像没有什么影响,所以直接正向遍历先跑跑看
代码
total = [] # 储存所有矩阵的行和列个数
n = int(input()) # 代表输入的矩阵个数
t_round = 0
deal = [] # 储存每一个矩阵的代价值的数组,代价值的解释: 由于每个矩阵相乘是A.rows乘B.columns,所以代价值就等于每一个矩阵的列,不过第一个矩阵还需要带上自己的行
for _ in range(n):
temp = list(map(int, input().split())) # 对应输入矩阵的行和列个数
total.append(temp)
if t_round == 0:
deal = [temp[0]] # 初始化,存储第一个矩阵的行, deal数组的长度为n + 1个
deal.append(temp[1])
t_round += 1
memo = [[float("inf")] * n for i in range(n)] # 存储中间值的
# 初始化结束,现在开始做题,优先计算对角线方向上的(实际手算的话,但是在这题里面,是横向计算,不断更新)
for i in range(n - 1, -1, -1):
rows1, columns1 = total[i]
price1 = deal[i]
for j in range(i, n):
if i == j: # dp数组的初始化
memo[i][j] = 0
continue
price2 = deal[j + 1]
rows2, columns2 = total[j]
for k in range(i, j):
price_k = deal[k + 1]
memo[i][j] = min(memo[i][j], memo[i][k] + memo[k + 1][j] + price1 * price_k * price2)
for i in memo:
print(i)
标签:deal,导论,memo,矩阵,括号,数组,乘法
From: https://blog.csdn.net/2302_79349465/article/details/139245248