动态规划之矩阵连乘问题

【问题描述】

给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2…,n-1。如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。例如,给定三个连乘矩阵{A1,A2,A3}的维数分别是10100,1005和550,采用(A1A2)A3,乘法次数为101005+10550=7500次,而采用A1(A2A3),乘法次数为100550+10100*50=75000次乘法,显然,最好的次序是(A1A2)A3,乘法次数为7500次。

问题分析

分析:
矩阵链乘法问题描述:
给定由n个矩阵构成的序列{A1,A2,…,An},对乘积A1A2…An,找到最小化乘法次数的加括号方法。

1)寻找最优子结构
此问题最难的地方在于找到最优子结构。对乘积A1A2…An的任意加括号方法都会将序列在某个地方分成两部分,也就是最后一次乘法计算的地方,我们将这个位置记为k,也就是说首先计算A1…Ak和Ak+1…An,然后再将这两部分的结果相乘。
最优子结构如下:假设A1A2…An的一个最优加括号把乘积在Ak和Ak+1间分开,则前缀子链A1…Ak的加括号方式必定为A1…Ak的一个最优加括号,后缀子链同理。
一开始并不知道k的确切位置,需要遍历所有位置以保证找到合适的k来分割乘积。

2)构造递归解
设m[i,j]为矩阵链Ai…Aj的最优解的代价,则可以得到状态转移方程
在这里插入图片描述
我们既然已经得到了状态转移方程,就要考虑怎么用程序来实现,这里我们用一个二维数组s[i][j]=k来更新区间ij这一段链是从k这个位置来分割得到最优解的,我们用二维数组dp[i][j]的值来表示区间[i , j]的最优解,要想状态方程能够正常的计算,我们首先需要先把计算得范围从小到大不停地计算,这样在计算后面范围较大时候的最优解是由前面两个状态转移来的,这样就能直接拿来使用,如下图
在这里插入图片描述
自顶向下的代码实现


#include<bits/stdc++.h>
#define N 7
using namespace std;
typedef long long ll;
const int maxn=7;
const int inf=0x3f3f3f3f3f;
void PrinT(int s[N][N],int i,int j)//递归打印
{
    if(i==j)//出口
    {
        cout<<"A"<<i;
    }
    else
    {
        cout<<"(";
        Print(s,i,s[i][j]);
        Print(s,s[i][j]+1,j);
        cout<<")";
    }
}
int main()
{
    int s[maxn][maxn],dp[maxn][maxn];//s[i][j]的值代表ij区间长度分割的点,dp[i][j],代表ij区间的最优解
     int p[N]={30,35,15,5,10,20,25};
     for(int i=1;i<=6;i++)//初始化为单个矩阵,没有连乘次数,所以初始胡为0;
        dp[i][i]=0;
        int n=6;
     for(int l=2;l<=n;l++)//l表示矩阵的长度
     {
         for(int i=1;i<=n-l+1;i++)//ij链起始的值
         {
             int j=l+i-1;//ij链尾
             dp[i][j]=inf;//初始化为无穷大
             for(int k=i;k<=j-1;k++)//遍历每个能分割的地方
             {
                 int q=dp[i][k]+dp[k+1][j]+p[i-1]*p[k]*p[j];//前面状态已经算出来了,故可以直接使用
                 if(q<dp[i][j])//更新
                 {
                     dp[i][j]=q;
                     s[i][j]=k;//ij链从k处分割的结果最优
                 }
             }
         }
     }
     cout<<dp[1][6]<<endl;

    Print(s,1,N-1);



}

完啦!

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值