矩阵链乘法(算法导论课后习题,动态规划)

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;

5103125506
0123456
子链长度 l子问题的解 m [i, j]k 的可能取值s [i, j] = k 取值m [i, j] 对应取值
2m [1,2]11150
m [2,3]21360
m [3,4]31180
m [4,5]413000
m [5,6]511500
3m [1,3]1,21960
2330
m [2,4]2,32330
3960
m [3,5]3,434800
4930
m [4,6]4,541860
56600
4m [1,4]1,2,31580
2405
3630
m [2,5]2,3,422430
39360
42830
m [3,6]3,4,532076
41770
51830
5m [1,5]1,2,3,414930
21830
36330
41655
m [2,6]2,3,4,521950
32940
42130
55430
6m [1,6]1,2,3,4,512250
22010
32550
42055
53155

根据最终 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)))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

g-zh

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

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

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

打赏作者

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

抵扣说明:

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

余额充值