矩阵连乘-动态规划
Posted xxmmqg
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了矩阵连乘-动态规划相关的知识,希望对你有一定的参考价值。
问题描述
给定n个矩阵({ A_1,A_2,A_3 dots, A_n }),其中(A_i)与(A_{i+1})是可乘的,(i=1,2,3,dots, n-1)。考察这n个矩阵的连乘积(A_1A_2dots A_n),由于矩阵乘法满足结合律,所以计算矩阵的连乘可以有许多不同的计算次序。这种计算次序可以用加括号的方式来确定。
分析最优解结构
设dp[i][j]
为(A_iA_{i+1}dots A_j)的最少乘积次数,并设(A_iA_{i+1}dots A_j)的最优计算次序从矩阵(A_k)和(A_{k+1})之间断开,(ileq k < j)。那么加括号方式为(((A_iA_{i+1}dots A_k)(A_{k+1}dots A_{j-1}A_{j})))。
最优总计算量dp[i][j] = dp[i][k] + dp[k+1][j] + A[i:k]与A[k+1:j]乘积次数
。其中A[i:k]
为(A_iA_{i+1}dots A_k)得到的矩阵,A[k+1][j]
同理。
得出,矩阵连乘计算次序的最优解包含着其子问题的最优解。这种性质称为最优子结构性质。
解题思路
我们首先默认矩阵的索引从0开始,即(A_0A_1dots A_{n-1})。
创建一个数组dp
,dp[i][j]
用来保存矩阵(A_idots A_j)的最优乘积次数,在创建一个数组p
,用来保存所有矩阵的行列数目。例如上图中的例子,它们的行列数目在p
中保存为[50,10,40,30,5]
。
根据最优解结构的分析,可以得出动态规划的递推公式为:
为了保证每次计算dp[i][j]
时,任意的pd[i][k]
和pd[k+1][j]
都已经得到结果,我们需要首先计算主对角线,然后再依次计算次对角线。
目前我们只是知道了最优计算次数,但是还不知道如何加括号。下面我们再定义一个数组s
与dp
形状相同,用来记录矩阵(A_idots A_j)之间的最优分割位置k
。最后通过递归找到最优加括号位置。
C++代码
#include<iostream>
#include<vector>
using namespace std;
void Traceback(int i,int j,vector<vector<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;
cin>>N;
vector<vector<int> >dp(N, vector<int>(5, 0));
vector<vector<int> >s(N, vector<int>(5, -1)); // 不使用普通数组是为了避免二维数组作为形参时的麻烦
vector<int>p(N+1);
for(int i=0; i<=N; i++)
cin>>p[i];
for(int n=1; n<N; n++)
{
int i = 0, j = n;
while(j<N && i<N)
{
dp[i][j] = 0x7fffffff;
int val;
for(int k=i; k<=j; k++)
{
val = dp[i][k]+dp[k+1][j]+p[i]*p[k+1]*p[j+1];
if(val < dp[i][j])
{
dp[i][j] = val;
s[i][j] = k;
}
}
i++; j++;
}
}
Traceback(0, N-1, s); // 加括号
cout<<dp[0][N-1];
return 0;
}
以上是关于矩阵连乘-动态规划的主要内容,如果未能解决你的问题,请参考以下文章