15.2-1 对矩阵规模序列 ⟨ 5 , 10 , 3 , 12 , 5 , 50 , 6 ⟩ \langle 5, 10, 3, 12, 5, 50, 6 \rangle ⟨5,10,3,12,5,50,6⟩,求矩阵链最优括号化方案。
矩阵规模序列 ⟨ 5 , 10 , 3 , 12 , 5 , 50 , 6 ⟩ \langle 5, 10, 3, 12, 5, 50, 6 \rangle ⟨5,10,3,12,5,50,6⟩ 对应 == A1(5*10)、A2(10*3)、A3(3*12)、A4(12*5)、A5(5*50)、A6(50*6)== 这样的 6 个矩阵连续相乘,求对应的矩阵链最优括号化方案。
MATRIX-CHAIN-ORDER(p)
n = p.length - 1 // 矩阵规模序列长度减一,也就是实际连乘的矩阵数量
let m[1..n,1..n] and s[1..n-1,2..n] be new tables
for i = 1 to n
m[i,i] = 0 // 单个矩阵不需要乘法运算,标量乘法次数为0
for l = 2 to n // l是子链长度
for i = 1 to n-l+1 // i是子链在原始矩阵链中的起点
j = i + l - 1 // j是根据子链长度l和子链起点i确定的子链终点
m[i,j] = ∞
for k = i to j-1
q = m[i,k] + m[k+1,j] + p[i-1]*p[k]*p[j]
if q < m[i,j]
m[i,j] = q // 存储子问题的解,也就是具体的标量乘法次数
s[i,j] = k // 存储具体括号方案,也就是括号具体位置
return m and s
上述伪代码的 C++实现:
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
// 辅助函数:打印最优括号方案
void PrintOptimalParens(const vector<vector<int>>& s, int i, int j) {
if (i == j) {
cout << "A" << i;
}
else {
cout << "(";
PrintOptimalParens(s, i, s[i][j]);
PrintOptimalParens(s, s[i][j] + 1, j);
cout << ")";
}
}
// 矩阵链乘法最优解算法
void MatrixChainOrder(const vector<int>& p) {
int n = p.size() - 1; // 矩阵数量
// 创建动态规划数组
vector<vector<int>> m(n + 1, vector<int>(n + 1, 0));
vector<vector<int>> s(n + 1, vector<int>(n + 1, 0));
// 子链长度从2开始,最大到n
for (int l = 2; l <= n; l++) {
// 遍历所有可能的子链起点
for (int i = 1; i <= n - l + 1; i++) {
int j = i + l - 1; // 子链终点
m[i][j] = INT_MAX; // 初始化为最大值
// 尝试所有可能的分割点k
for (int k = i; k < j; k++) {
int q = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];
if (q < m[i][j]) {
m[i][j] = q; // 更新最小代价
s[i][j] = k; // 记录最优分割点
}
}
}
}
// 打印最小代价
cout << "最小标量乘法次数: " << m[1][n] << endl;
// 打印最优括号方案
cout << "最优括号方案: ";
PrintOptimalParens(s, 1, n);
cout << endl;
}
int main() {
// 给定的矩阵维度序列:5, 10, 3, 12, 5, 50, 6
vector<int> p = { 5, 10, 3, 12, 5, 50, 6 };
MatrixChainOrder(p);
return 0;
}
当子链长度为:2时:
m[1,2]:
k取1时:
q = m[1,1] + m[2,2] + p[0]p[1]p[2]
q = 0 + 0 + 150 = 150
更新: m[1,2] = 150
m[2,3]:
k取2时:
q = m[2,2] + m[3,3] + p[1]p[2]p[3]
q = 0 + 0 + 360 = 360
更新: m[2,3] = 360
m[3,4]:
k取3时:
q = m[3,3] + m[4,4] + p[2]p[3]p[4]
q = 0 + 0 + 180 = 180
更新: m[3,4] = 180
m[4,5]:
k取4时:
q = m[4,4] + m[5,5] + p[3]p[4]p[5]
q = 0 + 0 + 3000 = 3000
更新: m[4,5] = 3000
m[5,6]:
k取5时:
q = m[5,5] + m[6,6] + p[4]p[5]p[6]
q = 0 + 0 + 1500 = 1500
更新: m[5,6] = 1500
-------------------------------------------
当子链长度为:3时:
m[1,3]:
k取1时:
q = m[1,1] + m[2,3] + p[0]p[1]p[3]
q = 0 + 360 + 600 = 960
更新: m[1,3] = 960
k取2时:
q = m[1,2] + m[3,3] + p[0]p[2]p[3]
q = 150 + 0 + 180 = 330
更新: m[1,3] = 330
m[2,4]:
k取2时:
q = m[2,2] + m[3,4] + p[1]p[2]p[4]
q = 0 + 180 + 150 = 330
更新: m[2,4] = 330
k取3时:
q = m[2,3] + m[4,4] + p[1]p[3]p[4]
q = 360 + 0 + 600 = 960
m[3,5]:
k取3时:
q = m[3,3] + m[4,5] + p[2]p[3]p[5]
q = 0 + 3000 + 1800 = 4800
更新: m[3,5] = 4800
k取4时:
q = m[3,4] + m[5,5] + p[2]p[4]p[5]
q = 180 + 0 + 750 = 930
更新: m[3,5] = 930
m[4,6]:
k取4时:
q = m[4,4] + m[5,6] + p[3]p[4]p[6]
q = 0 + 1500 + 360 = 1860
更新: m[4,6] = 1860
k取5时:
q = m[4,5] + m[6,6] + p[3]p[5]p[6]
q = 3000 + 0 + 3600 = 6600
-------------------------------------------
当子链长度为:4时:
m[1,4]:
k取1时:
q = m[1,1] + m[2,4] + p[0]p[1]p[4]
q = 0 + 330 + 250 = 580
更新: m[1,4] = 580
k取2时:
q = m[1,2] + m[3,4] + p[0]p[2]p[4]
q = 150 + 180 + 75 = 405
更新: m[1,4] = 405
k取3时:
q = m[1,3] + m[4,4] + p[0]p[3]p[4]
q = 330 + 0 + 300 = 630
m[2,5]:
k取2时:
q = m[2,2] + m[3,5] + p[1]p[2]p[5]
q = 0 + 930 + 1500 = 2430
更新: m[2,5] = 2430
k取3时:
q = m[2,3] + m[4,5] + p[1]p[3]p[5]
q = 360 + 3000 + 6000 = 9360
k取4时:
q = m[2,4] + m[5,5] + p[1]p[4]p[5]
q = 330 + 0 + 2500 = 2830
m[3,6]:
k取3时:
q = m[3,3] + m[4,6] + p[2]p[3]p[6]
q = 0 + 1860 + 216 = 2076
更新: m[3,6] = 2076
k取4时:
q = m[3,4] + m[5,6] + p[2]p[4]p[6]
q = 180 + 1500 + 90 = 1770
更新: m[3,6] = 1770
k取5时:
q = m[3,5] + m[6,6] + p[2]p[5]p[6]
q = 930 + 0 + 900 = 1830
-------------------------------------------
当子链长度为:5时:
m[1,5]:
k取1时:
q = m[1,1] + m[2,5] + p[0]p[1]p[5]
q = 0 + 2430 + 2500 = 4930
更新: m[1,5] = 4930
k取2时:
q = m[1,2] + m[3,5] + p[0]p[2]p[5]
q = 150 + 930 + 750 = 1830
更新: m[1,5] = 1830
k取3时:
q = m[1,3] + m[4,5] + p[0]p[3]p[5]
q = 330 + 3000 + 3000 = 6330
k取4时:
q = m[1,4] + m[5,5] + p[0]p[4]p[5]
q = 405 + 0 + 1250 = 1655
更新: m[1,5] = 1655
m[2,6]:
k取2时:
q = m[2,2] + m[3,6] + p[1]p[2]p[6]
q = 0 + 1770 + 180 = 1950
更新: m[2,6] = 1950
k取3时:
q = m[2,3] + m[4,6] + p[1]p[3]p[6]
q = 360 + 1860 + 720 = 2940
k取4时:
q = m[2,4] + m[5,6] + p[1]p[4]p[6]
q = 330 + 1500 + 300 = 2130
k取5时:
q = m[2,5] + m[6,6] + p[1]p[5]p[6]
q = 2430 + 0 + 3000 = 5430
-------------------------------------------
当子链长度为:6时:
m[1,6]:
k取1时:
q = m[1,1] + m[2,6] + p[0]p[1]p[6]
q = 0 + 1950 + 300 = 2250
更新: m[1,6] = 2250
k取2时:
q = m[1,2] + m[3,6] + p[0]p[2]p[6]
q = 150 + 1770 + 90 = 2010
更新: m[1,6] = 2010
k取3时:
q = m[1,3] + m[4,6] + p[0]p[3]p[6]
q = 330 + 1860 + 360 = 2550
k取4时:
q = m[1,4] + m[5,6] + p[0]p[4]p[6]
q = 405 + 1500 + 150 = 2055
k取5时:
q = m[1,5] + m[6,6] + p[0]p[5]p[6]
q = 1655 + 0 + 1500 = 3155
-------------------------------------------
最小标量乘法次数: 2010
最优括号方案: ((A1A2)((A3A4)(A5A6)))
根据 P213 的伪代码,先遍历子链长度为 2 的情况,m [1,2], 此时可能的括号方案 k 只能取 1,q = p [0]*p [1]*p [2] = 150 < m [1,2] = ∞ \infty ∞,所以 m [1,2] = 150; 依此类推,m [2,3] = 360、m [3,4] = 180、m [4,5] = 3000、m [5,6] = 1500,两个矩阵相乘,不存在更优括号方案,就是直接相乘的运算量;再遍历子链长度为 3 的情况,m [1,3],此时可能的括号方案 k 可能取 1 或 2,k 取 1 时,q = m [1,1]+m [2,3]+p [0] p [1] p [3] = 960 < m [1,3] = ∞ \infty ∞,所以 m [1,3] = 960;k 取 2 时,q = m [1,2]+m [3,3]+p [0] p [2] p [3] = 330 < m [1,3] = 960,所以 m [1,3] = 330;依此类推,对 m [2,4]、m [3,5]、m [4,6] 依次取 k = 1、k = 2(遍历当前子链长度下的所有可能括号方案),然后存储更优的标量乘法次数 m [i, j] 和具体括号方案 s [i, j].
m [2,4],k 取 2 时,q = m [2,2]+m [3,4]+p [1] p [2] p [4] = 330 <m[2,4]= ∞ \infty ∞,所以m[2,4]=330;k取3时,q=m[2,3]+m[4,4]+p[1]p[3]p[4]=960> m [2,4] = 330,所以 m [2,4] = 330;
m [3,5],k 取 3 时,q = m [3,3]+m [4,5]+p [2] p [3] p [5] = 4800 < m [3,5] = ∞ \infty ∞,所以 m [3,5] = 4800;k 取 4 时,q = m [3,4]+m [5,5]+p [2] p [4] p [5] = 930 < 4800,所以 m [3,5] = 930;
m [4,6],k 取 4 时,q = m [4,4]+m [5,6]+p [3] p [4] p [6] = 1860 <m[4,6]= ∞ \infty ∞,所以m[4,6]=1860;k取5时,q=m[4,5]+m[6,6]+p[3]p[5]p[6]=6600> 1860,所以 m [4,6] = 1860;
再遍历子链长度为 4 的情况
m [1,4],k 取 1 时,q = m [1,1]+m [2,4]+p [0] p [1] p [4] = 580 <m[1,4]= ∞ \infty ∞,所以m[1,4]=580;k取2时,q=m[1,2]+m[3,4]+p[0]p[2]p[4]=405<580,所以m[1,4]=405;k取3时,q=m[1,3]+m[4,4]+p[0]p[3]p[4]=630> 405,所以 m [1,4] = 405;
m [2,5],k 取 2 时,q = m [2,2]+m [3,5]+p [1] p [2] p [5] = 1770 <m[2,5]= ∞ \infty ∞,所以m[2,5]=1770;k取3时,q=m[2,3]+m[4,5]+p[1]p[3]p[5]=9360> 1770,所以 m [2,5] = 1770;k 取 4 时,q = m [2,4]+m [5,5]+p [1] p [4] p [5] = 2830 > 1770,所以 m [2,5] = 1770;
m [3,6],k 取 3 时,q = m [3,3]+m [4,6]+p [2] p [3] p [6] = 2076 <m[3,6]= ∞ \infty ∞,所以m[3,6]=2076;k取4时,q=m[3,4]+m[5,6]+p[2]p[4]p[6]=1770<2076,所以m[3,6]=1770;k取5时,q=m[3,5]+m[6,6]+p[2]p[5]p[6]=1830> 1770,所以 m [3,6] = 1770;
再遍历子链长度为 5 的情况
m [1,5],k 取 1 时,q = m [1,1]+m [2,5]+p [0] p [1] p [5] = 4930 <m[1,5]= ∞ \infty ∞,所以m[1,5]=4930;k取2时,q=m[1,2]+m[3,5]+p[0]p[2]p[5]=1830<4930,所以m[1,5]=1830;k取3时,q=m[1,3]+m[4,5]+p[0]p[3]p[5]=6330> 1830;所以 m [1,5] = 1830;k 取 4 时,q = m [1,4]+m [5,5]+p [0] p [4] p [5] = 1655 < 1830,所以 m [1,5] = 1655;
m [2,6],k 取 2 时,q = m [2,2]+m [3,6]+p [1] p [2] p [6] = 1950 <m[2,6]= ∞ \infty ∞,所以m[2,6]=1950;k取3时,q=m[2,3]+m[4,6]+p[1]p[3]p[6]=2940> 1950,所以 m [2,6] = 1950;k 取 4 时,q = m [2,4]+m [5,6]+p [1] p [4] p [6] = 2130 > 1950;所以 m [2,6] = 1950;k 取 5 时,q = m [2,5]+m [6,6]+p [1] p [5] p [6] = 5430 > 1950,所以 m [2,6] = 1950;
再遍历子链长度为 6 的情况
m [1,6],k 取 1 时,q = m [1,1]+m [2,6]+p [0] p [1] p [6] = 2250 <m[1,6]= ∞ \infty ∞,所以m[1,6]=2250;k取2时,q=m[1,2]+m[3,6]+p[0]p[2]p[6]=2010<2250,所以m[1,6]=2010;k取3时,q=m[1,3]+m[4,6]+p[0]p[3]p[6]=2550> 2010,所以 m [1,6] = 2010;k 取 4 时,q = m [1,4]+m [5,6]+p [0] p [4] p [6] = 2055 > 2010,所以 m [1,6] = 2010;k 取 5 时,q = m [1,5]+m [6,6]+p [0] p [5] p [6] = 3155 > 2010,所以 m [1,6] = 2010;
5 | 10 | 3 | 12 | 5 | 50 | 6 |
---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 |
子链长度 l | 子问题的解 m [i, j] | k 的可能取值 | s [i, j] = k 取值 | m [i, j] 对应取值 |
---|---|---|---|---|
2 | m [1,2] | 1 | 1 | 150 |
m [2,3] | 2 | 1 | 360 | |
m [3,4] | 3 | 1 | 180 | |
m [4,5] | 4 | 1 | 3000 | |
m [5,6] | 5 | 1 | 1500 | |
3 | m [1,3] | 1,2 | 1 | 960 |
2 | 330 | |||
m [2,4] | 2,3 | 2 | 330 | |
3 | 960 | |||
m [3,5] | 3,4 | 3 | 4800 | |
4 | 930 | |||
m [4,6] | 4,5 | 4 | 1860 | |
5 | 6600 | |||
4 | m [1,4] | 1,2,3 | 1 | 580 |
2 | 405 | |||
3 | 630 | |||
m [2,5] | 2,3,4 | 2 | 2430 | |
3 | 9360 | |||
4 | 2830 | |||
m [3,6] | 3,4,5 | 3 | 2076 | |
4 | 1770 | |||
5 | 1830 | |||
5 | m [1,5] | 1,2,3,4 | 1 | 4930 |
2 | 1830 | |||
3 | 6330 | |||
4 | 1655 | |||
m [2,6] | 2,3,4,5 | 2 | 1950 | |
3 | 2940 | |||
4 | 2130 | |||
5 | 5430 | |||
6 | m [1,6] | 1,2,3,4,5 | 1 | 2250 |
2 | 2010 | |||
3 | 2550 | |||
4 | 2055 | |||
5 | 3155 |
根据最终 m [1,6] 的执行过程,在 k = 2 分割时计算量最小,所以分为 m [1,2] 和 m [3,6],m [1,2] 为两个矩阵相乘,已经无法再分割,m [3,6] 可以在 k = 4 分割时计算量最小,所以最终总的方案就是在 k = 2 和 k = 4 两处分割
因此最终最优方案如下:最小标量乘法次数: 2010;最优括号方案: ((A1A2)((A3A4)(A5A6)))