⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 矩阵连乘问题.cpp

📁 算法分析部分
💻 CPP
字号:
#include<iostream>
using namespace std;
/*
给定n个矩阵{A1,A2,...,An},其中Ai与Ai+1是可乘的,i = 1,2...,n-1。我们
要计算出这n个矩阵的连乘积A1A2...An。
*/
/*
分析最优子结构:
设计求解具体问题的动态规划算法的第一步是刻画该问题的
最优结构的特征。为方便,将矩阵连乘AiAi+1...Aj简记为A[i:j]。
我们来看计算A[1:n]的一个最优次序。设这个计算次序在矩阵Ak和Ak+1之间将矩阵链断开
,1<=k<n,则完全加括号方式为((A1...Ak)(Ak+1...An))。依此顺序
,我们先计算A[1:k]和A[k+1:n],然后将计算结果相乘得到A[1:n].

问题的关键特征是:计算A[1:n]的一个最优次序所包含的计算矩阵A[1:k]和A[k+1:n]的次序也是最
优的。事实上,若有一个计算A[1:k]的次序需要的计算量更少,则用此次序替换原来的计算A[1:k]
的次序,得到的计算A[1:n]的次序需要的计算量将比最优次序所需要的计算量更少,这是一个矛盾。

因此,矩阵连乘计算次序问题的最优解包含着其子问题的最优解。这种性质称为最优子结构性质。
一个问题的最优子结构性质,是该问题能够用动态规划算法求解的显著特征。
*/

/*
建立递归关系:
设计一个动态规划算法的第二步是递归地定义最优值。对于矩阵连乘积的最优计算次序
问题,设计算A[i:j],1<=i<=j<=n,所需的最少数乘次数为m[i][j],则原问题的最优
值为m[1][n]。
m[i][j]可以递归地定义为:
m[i][j]=0 (i==j)
m[i][j]=min{m[i][k] + m[k+1][j] + Pi-1PmPj}i<=k<j

若将对应于m[i][j]的断开位置k记为s[i][j],在计算出最优值
m[i][j]后,可递归地由s[i][j]构造出相应的最优解
*/

/*
计算最优值
根据计算m[i][j]的递归式,容易写一个递归算法来计算m[1][n]
在递归计算的过程中,不同的子问题的个数只有O(n^2)个。事实上,对于1<=i<=j<=n不同的
有序对(i,j)对应于不同的子问题。因此不同子问题的个数最多只有(n,2)+n=O(n^2)个。
递归计算时,许多子问题被重复计算多次。这也是该问题可用动态规划算法求解的又一显著
特征。

用动态规划算法解此问题时,可依据其递归式以自底向上的方式进行计算。在计算过程中,
保存已经解决的子问题答案。每个子问题只计算一次,而在后面需要时只要简单查一下,从而
避免了大量的重复计算,最终得到多项式的时间算法。下面所给出的计算m[i][j]的动态规划算法
中,输入参数{p0,p1,...,pn}存储于数组p中。算法除了输出最优数组m外,还输出记录最优断开
位置的数组s
*/

//p数组记录的是每个矩阵的维数
void MatrixChain( int *p,int n,int **m,int **s )
{
	for( int i = 1; i <= n; ++i )
	{
		m[i][i] = 0;
	}
	
	for( int r = 2; r <= n; ++r )
	{
		for( int i = 1; i <= n - r + 1; ++i )
		//是为了只计算i:j的最小乘法次数,其中要求i<j,因为i>j没意义
		{
			int j = i + r - 1;
			m[i][j] = m[i+1][j] + p[i-1]*p[i]*p[j];
			s[i][j] = i;
			cout<<s[i][j]<<" "<<endl;
			for( int k = i + 1; k < j; ++k )
			{
				int t = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j];
				if ( t < m[i][j] )
				{
					m[i][j] = t;
					s[i][j] = k;
				}
			}
		}
	}
}

/*
构造最优解
动态规划算法的第4步是构造问题的一个最优解。算法MatrixChain只是计算出了最优
值,并位给出最优解。

MatrixChain已记录了构造一个最优解所需要的全部信息。事实上,s[i][j]中的数k告诉我们
计算矩阵链A[i:j]的最佳方式应在矩阵Ak和Ak+1之间断开,即最优的加括号方式应为(A[i:k]
)(A[k+1:j])。因此,动s[1][n]中记录的信息可知计算A[1:n]的最优加括号方式为
(A[1:s[1][n])(A[s[1][n]+1:n)。
*/

/*
下面的算法Traceback按算法MaxtrixChain计算出的断点矩阵s
指示的加括号方式输出计算A[i:j]的最优计算顺序。
*/

void Trackback( int i, int j, int **s )
{
	if ( i == j )
	{
		return;
	}
	Trackback( i, s[i][j], s );
	Trackback( s[i][j] + 1, j, s );
	cout<<"i is : "<<i<<endl;
	cout<<"j is : "<<j<<endl;
	cout<<"Multiply A["<<i<<":"<<s[i][j]<<"]"<<endl;
	cout<<"and A["<<(s[i][j]+1)<<":"<<j<<"]"<<endl;
	
}

int main()
{
	int n = 6;
	int *p = new int[7];
	p[1] = 35;
	p[2] = 15;
	p[3] = 5;
	p[4] = 10;
	p[5] = 20;
	p[6] = 25;

	int **m = new int*[7];
	int **s = new int*[7];
	for( int i = 0; i < 7; ++i )
	{
		m[i] = new int[7];
		s[i] = new int[7];
		for( int j = 0; j < 7; ++j )
		{
			m[i][j] = 0;
			s[i][j] = 0;
		}
	}

	MatrixChain( p,6,m,s );
	for( int t = 0; t < 7; ++t )
	{
		for( int q = 0; q < 7; ++q )
		{
			cout<<s[t][q]<<" ";
		}
		cout<<endl;
	}
	Trackback( 1, 6, s );

	return 0;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -