矩阵最优连乘问题(区间DP+记忆化)

Posted lipoicyclic

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了矩阵最优连乘问题(区间DP+记忆化)相关的知识,希望对你有一定的参考价值。

Description

在科学计算中经常要计算矩阵的乘积。矩阵A和B可乘的条件是矩阵A的列数等于矩阵B的行数。若A是一个p×q的矩阵,B是一个q×r的矩阵,则其乘积C=AB是一个p×r的矩阵。

由公式知计算C=AB总共需要pqr次的数乘。

为了说明在计算矩阵连乘积时加括号方式对整个计算量的影响,我们来看一个计算3个矩阵{A1,A2,A3}的连乘积的例子。设这3个矩阵的维数分别为10×100,100×5和5×50。若按第一种加括号方式((A1A2)A3)来计算,总共需要10×100×5+10×5×50=7500次的数乘。若按第二种加括号方式(A1(A2A3))来计算,则需要的数乘次数为100×5×50+10×100×50=75000。第二种加括号方式的计算量是第一种加括号方式的计算量的10倍。由此可见,在计算矩阵连乘积时,加括号方式,即计算次序对计算量有很大影响。

于是,人们自然会提出矩阵连乘积的最优计算次序问题,即对于给定的相继n个矩阵{A1,A2,…,An}(其中Ai的维数为pi-1×pi ,i=1,2,…,n),如何确定计算矩阵连乘积A1A2…An的一个计算次序(完全加括号方式),使得依此次序计算矩阵连乘积需要的数乘次数最少。

输入数据第一行为矩阵个数,第二行为n+1个数字a[0~n],其中对于第i个矩阵其大小为a[i-1]*a[i]。

 

刚刚学习了区间DP,写发题解纪念一下。

按照区间DP的思想,可以倒着分析,枚举最后一次进行乘法的位置,将原区间转化为两个子区间,相应代表了将原问题转化为两个规模较小的子问题。这里可以将递归和记忆化结合起来,递归的终点就是区间左端点等于右端点,由于提前给dp[i][i]赋值为0,所以这里直接return 0;同时如果dp[l][r]不为初始化时的0x3f3f3f3f的话说明这个值已经得到了就不用再算一遍了,直接return就好(记忆化)。然后就是dp[l][r]还没被计算过,那么枚举每个可能为当前区间最后一次做乘法的位置:l~r-1,然后写转移方程dp[l][r]=min(dp[l][r],process(l,i)+process(i+1,r)+a[l-1]*a[i]*a[r])(注意这里的dp[l][r]不要写成process(l,r)要不然永远无法更新,直接死循环了),至于为啥加的是a[l-1]*a[i]*a[r],这是根据矩阵乘法的性质得来的,分开的左半个矩阵的大小为a[l-1]*a[i],右半个矩阵的大小为a[i]*a[r],算出来后直接return即可。最终要求的就是process(1,n)。

#include <bits/stdc++.h>
using namespace std;
int n,a[1005];//第i个矩阵是a[i-1]*a[i]大小 
int dp[1005][1005]={0};
int process(int l,int r)
{
    if(dp[l][r]!=0x3f3f3f3f)return dp[l][r];
    int i;
    for(i=l;i<r;i++)
    {
        dp[l][r]=min(dp[l][r],process(l,i)+process(i+1,r)+a[l-1]*a[i]*a[r]);//这里的dp[l][r]不要写成process(l,r)要不然永远无法更新,直接死循环了 
    }
    return dp[l][r];
}
int main()
{
    cin>>n;
    int i;
    for(i=0;i<=n;i++)scanf("%d",&a[i]);
    memset(dp,0x3f3f3f3f,sizeof(dp));
    for(i=1;i<=n;i++)dp[i][i]=0;
    printf("%d",process(1,n));
    return 0;
}

 

以上是关于矩阵最优连乘问题(区间DP+记忆化)的主要内容,如果未能解决你的问题,请参考以下文章

矩阵连乘 /// 区间DP oj1900

POJ 1191 棋盘分割 (区间DP,记忆化搜索)

Uva 10003 Cutting Sticks (类似于最优矩阵连乘的dp)

矩阵连乘最优解---动态规划

tyvj 1198 矩阵连乘——区间dp

tyvj 1198 矩阵连乘——区间dp