快速计算----矩阵连乘--DP

给定 n 个矩阵{A1,A2,A3,…,An},其中,Ai 和 Ai+1(i=1,2,…,n−1)是可乘的。用加括号的方法表示矩阵连乘的次序,不同的计算次序计算量(乘法次数)是不同的,找出一种加括号的方法,使得矩阵连乘的计算量最小

例如:
A1 是 M5×10 的矩阵;
A2 是 M10×100 的矩阵;
A3 是 M100×2 的矩阵。
那么有两种加括号的方法:
(1)(A1 A2)A3;
(2)A1(A2 A3)。
第 1 种加括号方法运算量:5×10×100+5×100×2=6000。
第 2 种加括号方法运算量:10×100×2+5×10×2=2100。

可以看出,不同的加括号办法,矩阵乘法的运算次数可能有巨大的差别!

(一)

分析问题:根据矩阵的乘法规则:Am×n、An×k相乘执行乘法运算的次数为 m*n*k。

如果直接暴力穷举所有加括号的方法,那么加括号的所有方案是一个卡特兰数序列,其算法时间复杂度为2^n,因此我们需要推理一下看能否可以用动态规划。

(1)分析是否具有最优子结构

假设AiAi+1.....Aj的乘法次数是s,AiAi+1....Ak的乘法次数是x,Ak+1Ak+2.....Aj的乘法次数是y,(AiAi+1....Ak)和(Ak+1Ak+2.....Aj)的乘法次数是z,则s=x+y+z,只要用反证法证明一下可知:如果s是最优的,则x和y一定是最优的。

(二)建立最优值递归式

① :用m[i][j]表示AiAi+1....Aj矩阵连乘的最优值,那么两个子问题(AiAi+1....Ak)和(Ak+1Ak+2....Aj)对应的最优值分别是m[i][k]和m[k+1][j]。

②:而(AiAi+1....Ak)和(Ak+1Ak+2....Aj)的结果矩阵相乘的次数:

设Am的行数为Px,列数为qx,x=i,i+1,....,j。因为矩阵可乘是前一个矩阵的列数等于下一个矩阵的行数。

则(AiAi+1…Ak)的结果是一个 pi×qk矩阵,(Ak+1Ak+2…Aj)的结果是一个 pk+1*qj 矩阵,qk= pk+1,两个结果矩阵相乘的乘法次数是 pi*pk+1*qj。

③:递归式:

当i==j时:m[i][j]=0;

当i<j时,m[i][j]=min{m[i][k]+m[k+1][j]+Pi*Pk+1*Pj};

因为具有最优子结构性质,所以我们先求两个矩阵的最优值,再求3个矩阵想相乘的最优值,直到n个矩阵连乘的最优值。

(三)构造最优解

上述得到的最优值只是矩阵连乘的最小的乘法次数,并不知道加括号的次序,需要从记录表中往回追溯还原加括号,构造出最优解。用s[][]数组来存放各个子问题的加括号位置:

s[1][n] 表示 A1A2…An 最优解的加括号位置,即(A1A2…As[1][n])(As[1][n]+1…An),我们在递归构造两个子问题(A1A2…As[1][n])、(As[1][n]+1…An)的最优解加括号位置,一直递归到子问题只包含一个矩阵为止

代码详细:

#include<iostream>
#include<string.h>
using namespace std;
const int N = 10000;
int p[N], m[N][N], s[N][N];
int n;//矩阵个数
int matrixchain()
{
	memset(s, 0, sizeof(s));
	memset(m, 0, sizeof(m));
	for (int r = 2; r <= n; r++)//不同规模的子问题
	{
		for (int i = 1; i <= n - r + 1; i++)
		{
			int j = r + i - 1;
			m[i][j] = m[i + 1][j] + p[i - 1] * p[i] * p[j];//决策为k=i的乘法次数
			s[i][j] = i;
			for (int k = i + 1; k < j; k++)
			{
				int temp = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];
				if (temp < m[i][j])
				{
					m[i][j] = temp;
					s[i][j] = k;
				}
			}
		}
	}
	return m[1][5];
}
//追溯法
void print(int i,int j)
{
	if (i == j)
	{
		cout << "A[" << i << "]";
		return;
	}
	cout << "(";
	print(i, s[i][j]);
	print(s[i][j] + 1, j);
	cout << ")";
}
int main()
{
	cin >> n;
	for (int i = 0; i <= n; i++)
		cin >> p[i];//输入每个矩阵的行数和最后一个矩阵的列数
	cout << "最小计算量的值为:" << endl;
	cout << matrixchain() << endl;
	print(1, n);
	return 0;
}


猜你喜欢

转载自blog.csdn.net/weixin_41676901/article/details/80903531
今日推荐