动态规划:矩阵链连乘问题

动态规划之矩阵连乘问题
矩阵链乘问题简单而言,便是给定n个矩阵{A1,A2……An},其中Ai与Ai+1是可乘的,考察其连乘时进行计算的最小次数。

在计算过程中,不同的加括号方式对于整个计算量有不一样的影响,举例说明:设有三个矩阵{A1,A1,A3}的维数分别为10*100,100*5和5*50,若是按照((A1,A2)A3)顺序计算,最终的计算量为10*100*5+10*5*50 = 7500,若是按照(A1(A2,A3))顺序计算,最终结果为100*5*20+10*100*50=75000次数乘。所以加括号方式,即计算次序对于计算量有很大的影响。

为了求得最优计算顺序,很容易想到用穷举法来解决问题,但是如果列出所有的计算次序,便会出现很多的重复计算先行,十分的浪费时间,于此使用动态规划来解决问题,在解答过程中,大致分为几个步骤如下:

1.分析最优解结构

为方便描述,将矩阵连乘积简记为A[i:j],那么,若从k处把矩阵链断开,即((A1……Ak)(Ak……Aj))则计算A[i:j]的计算量为计算A[i:k]的计算量加上A[k:j]的计算量加上两者相乘的计算量。

于是问题的一个关键特征是:计算A[1:n]的最优次序包含的矩阵子链A[1:k]的计算量为计算A[k:n]的次序也是最优的,即问题的最优子结构性质,可以通过反证法来证明。

2.建立递归关系

设计算A[i:j],1≤i≤j≤n,所需要的最少数乘次数m[i:j],则原问题的最优值为m[1:n]         
当i=j时,A[i:j]=Ai,因此,m[i,i]=0,i=1,2,…,n
当i<j时,

可以递归地定义m[i,j]为:

k的位置只有j-1种可能

3.计算最优值
对于1≤i≤j≤n不同的有序对(i,j)对应于不同的子问题。因此,不同子问题的个数最多只有

由此可见,在递归计算时,许多子问题被重复计算多次。这也是该问题可用动态规划算法求解的又一显著特征。
用动态规划算法解此问题,可依据其递归式以自底向上的方式进行计算。在计算过程中,保存已解决的子问题答案。每个子问题只计算一次,而在后面需要时只要简单查一下,从而避免大量的重复计算,最终得到多项式时间的算法

4.样例

计算矩阵连乘积A1 A2 A3 A4 A5 A6,维数分别为 30*35,35*15,15*5,5*10,10*20,20*25

分别使用数组m和s分别存储其计算次数与计算的顺序

用动态规划求解时:

上代码

对于动态数组,借鉴大佬资料,链接在此

#include<iostream>
using namespace std;

void MatrixChain(int *p, int n, int **m,int **s)
{
    for(int i = 1; i <= n; i ++)
    {
        m[i][i] = 0;//单个矩阵计算量为0
    }
    for(int r = 2; r <= n; r++)//矩阵结合个数
    {
        for(int i = 1; i <= n-r+1; i ++)//可能的起点
        {
            int j = i + r - 1;//对应i包含r个矩阵的终点
            m[i][j] = m[i+1][j] + p[i-1] * p[i] * p[j];//A[i:j]=A[i:i]+A[i+1:j]
            s[i][j] = i;//断点i
            for(int k = i+1; k < j; k ++)
            {
                int t = m[i][k] + m[k+1][j] + p[i-1] * p[k] * p[j];
                if(m[i][j] > t)//较小值
                {
                    m[i][j] = t;
                    s[i][j] = k;
                }
            }

        }
    }
    cout <<  "计算次数为:" << m[1][n] << endl;
}

void Traceback(int i, int j, int **s)
{
    if(i == j)return;
    Traceback(i,s[i][j],s);
    Traceback(s[i][j]+1,j,s);
    cout << "Multiply A" << i << "," << s[i][j];
    cout << "and A" << (s[i][j]+1) << "," << j << endl;;
}

int main()
{
    int n;
    int ra,ca;
    int p[1001];
    cin >> n;
    int** m = new int*[n+1];
    m[0] = new int[(n+1)*(n+1)];
    int** s = new int*[n+1];
    s[0] = new int[(n+1)*(n+1)];
    //int s[100][100];
    for(int i = 1; i < n+1; i ++)
    {
        m[i] = m[i-1] + n+1;
        s[i] = s[i-1] + n+1;
    }
    for(int i = 0; i < n; i ++)
    {
        cin >> ra >> ca;
        p[i] = ra;
        if(i == n-1)p[i+1] = ca;
    }
    if(n == 1) cout << "计算次数为:0,Multiply A1,1" << endl;
    else{
        MatrixChain(p,n,m,s);
        Traceback(1,n,s);
    }
    return 0;
}


计算结果:15125,乘法顺序为:(A1(A2 A3))((A4 A5)A6)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值