动态规划——矩阵连乘问题

动态规划——矩阵连乘问题

题目: 给定n个矩阵{A1,A2,…,An}(n<=40),其中Ai与Ai+1是可乘的,i=1,2…,n。第i个矩阵的维数用pi−1,pi来表示。如何确

定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。例如,给定三个连乘矩阵{A1,A2,A3}的维数
数组p为:10,100,5,50,即分别是10 ×100,100×5和5×50,采用(A1A2)A3,乘法次数为10×100×5+10×5×50=7500次,而采用A1(A2A3),乘法次数为100×5×50+10×100×50=75000次乘法,显然,最好的次序是(A1A2)A3,乘法次数为7500次。

输入示例:

6
30 35 15 5 10 20 25

输出示例:

15125

解题思路分析:

矩阵相乘最少应该有两个矩阵相乘,一个矩阵是不存在相乘的问题的。那最多有多少个矩阵相乘呢?最多可以有n个矩阵相乘,至于n是多少我们也不知道。

题目也是让我们求n个矩阵相乘的问题,我们可以将n个矩阵相乘看成两个矩阵相乘,这里的大并不是指矩阵真的很大,文字不好描述,我举例说明一下:

例如求n个矩阵 (A1,A2,A3,…,An-1,An)相乘,我们可以将其看成矩阵(A1,A2,…,Ak)与矩阵(Ak+1,Ak+2,…,An)相乘,这里k的范围为:1<k<n。同地我们还可以将矩阵(A1,A2,…,Ak)与矩阵(Ak+1,Ak+2,…,An)又进一步细分。

像这样把求原问题的解转化为求子问题的解,我们可以考虑用动态规划来实现。

动态规划的解题步骤:

  • 明确目的状态:我们要求的目的状态是什么,就本题而言,我们的目的状态是求n个矩阵相乘的最小乘法次数

  • 明确dp数组的含义:dp数组是用来存放每一个状态下的值,具体题型具体分析,你要明白dp数组里面的值是什么意思,就本题而言,dp[i] [j]是用来存放矩阵(Ai,Ai+1,…,Aj)的最小相乘次数

  • 建立状态转移方程:每一个状态之间的关系,目的状态与它的上一个状态之间的关系,本题的状态转移方程为:
    d p [ i ] [ j ] = m i n d p [ i ] [ k ] + d p [ k + 1 ] [ j ] + p [ i − 1 ] p [ k ] p [ j ] ( i < j , i < = k < j ) dp[i][j]=min{ dp[i][k]+dp[k+1][j]+p[i-1]p[k]p[j] }(i<j , i<=k<j) dp[i][j]=mindp[i][k]+dp[k+1][j]+p[i1]p[k]p[j](i<j,i<=k<j)

解释:为什么要加上min,因为 i 到 j 之间 k 的取值有多个,k 的取值不同,产生的dp[i] [j] 的值不同,我们要求最小的相乘次数,所以要取最小的dp[i] [j]的值

整个动态规划解题步骤中,最关键最核心的是状态转移方程,那么状态转移方程是怎么来的呢?

状态转移方程是我们经过探讨、总结得出来的——目的状态与它的上一次状态的关系表达式

探讨过程:

假设n为3,求矩阵(A1, A2, A3)相乘的最小相乘次数,并且假设(A1A2)A3 这个相乘的次序所用的乘法次数最少,则在目的转态n为3下的最少相乘次数为p[0] * p[1] * p[2] +p[0] * p[2] * p[3] (p数组存放的是矩阵的维数,例A1的行为p[0] 列为p[1])

而根据我们的定义dp数组的定义,则有dp[1] [3] = p[0] * p[1] * p[2] +p[0] * p[2] * p[3] ;

因为(A1A2)A3 这个相乘的次序所用的乘法次数最少,所以矩阵A1A2相乘的次序所用的乘法次数也必然最少

则dp[1] [3] = dp[1] [2] +p[0] * p[2] * p[3] ①式

(p[0]是矩阵A1的行数,p[2],p[3]分别为矩阵A3的行数和列数)

设n为4,求矩阵(A1, A2, A3,A4)相乘的最小相乘次数,我们可以将其看成矩阵(A1, A2, A3)与矩阵A4相乘的最小相乘次数,也假设这样相乘是乘法次数最少的,则

目的状态为n=4的最小次数等于 目的状态为n=3的矩阵与矩阵A4相乘的最小次数 ,根据dp数组的含义,矩阵(A1, A2, A3)的最小相乘

次数为dp[1] [3], 根据上面对n=3的推导,dp[1] [4] = dp[1] [3] +p[0] * p[3] * p[4] ②式

又因为矩阵相乘最少应该有两个矩阵相乘,且根据dp数组的含义有:

如果 i = j, 则 dp[i] [j] =0;

对①②式变型有:
dp[1] [3] = dp[1] [2] + dp[3] [3] + p[0] * p[2] * p[3]
dp[1] [4] = dp[1] [3] + dp[4] [4] +p[0] * p[3] * p[4]

再多讨论一下n=5,n=6的情况,就可以得到我们的状态转移方程:
d p [ i ] [ j ] = m i n d p [ i ] [ k ] + d p [ k + 1 ] [ j ] + p [ i − 1 ] p [ k ] p [ j ] ( i < j , i < = k < j ) dp[i][j]=min{ dp[i][k]+dp[k+1][j]+p[i-1]p[k]p[j] }(i<j , i<=k<j) dp[i][j]=mindp[i][k]+dp[k+1][j]+p[i1]p[k]p[j](i<j,i<=k<j)

代码示例:

#include<bits/stdc++.h>
using namespace std;
int n;//n为矩阵的个数 
int p[41];//用来存放矩阵的行数和列数,例如矩阵Ai的行数为p[i-1]列数为p[i] 
int func();// 返回矩阵相乘的最小次数 
int main(){
	cin>>n;
	//因为矩阵有n个,所以p数组中应该有n+1个值 
	for(int i=0;i<=n;i++){
		cin>>p[i];
	}
	int num=func();
	cout<<num<<endl;
}
int func(){
	//vector容器 dp[n+1][n+1]相当于动态的二维数组, 
	//根据输入的n的值来自动开辟空间 ,其类型为int 
	vector< vector<int> > dp(n+1,vector<int>(n+1));
	for(int i=n;i>=1;i--){
		for(int j=i;j<=n;j++){
			if(i==j){
				dp[i][j]=0;
			}
			if(i<j){
				// 因为是求最小值,所以尽可能的将temp设得大一点 
				int temp=1000000;
				for(int k=i;k<j;k++){
					int ans=dp[i][k]+dp[k+1][j]+p[i-1]*p[k]*p[j];
					if(ans<temp){
						temp=ans;
					}
				}
				dp[i][j]=temp;
			}
		}
	}
	
	return dp[1][n];
} 

为方便理解,大家可以画一个n*n的方格,以n为4为例:

表格(1,2)表示矩阵A1A2相乘的最小次数:dp[1] [2]

表格(1,4)表示矩阵{A1A2A3A4}相乘的最小次数:dp[1] [4]

A1A2A3A4
A10dp[1] [2]dp[1] [3]dp[1] [4]
A20dp[2] [3]dp[2] [4]
A30dp[3] [4]
A40

根据状态转移方程,dp[1] [2] =dp[1] [1] +dp [2] [2] + p[0] * p[1] * p[2]

像这样的每一个目的状态都可以用上一个目的状态来表式

表格中只有在对角线的右边,才是具有实际意义的dp数组

如果在下面,例如dp[2] [1] 表示矩阵{A2A1}相乘的最小次数,A2A1改变了矩阵相乘的顺序,是不对的

根据矩阵知识A1A2 不等于 A2A1

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值