超详细的动态规划解决矩阵连乘

动态规划解决矩阵连乘

超详细的动态规划解决矩阵连乘_第1张图片
问题描述:
  给定 N N N个矩阵 { A 1 , A 2 , A 3 … … A n } \{A_1,A_2,A_3……A_n\} { A1,A2,A3An},其中 A i A_{i} Ai A i + 1 A_{i+1} Ai+1 和可以相乘的,即: A i A_i Ai 的行数等于 A i + 1 A_{i+1} Ai+1 的列数,如何。确定矩阵连乘的计算次序,使得安照此次序计算该矩阵连乘所需要的数乘次数最少。
  两个矩阵相乘,它的数乘次数等于前一个矩阵的行数乘以后一个矩阵的列数,当这些矩阵相乘的次序不同时,它的数乘次数的有很大差异的。如下例题:

例:现有四个矩阵 A , B , C , D A,B,C,D ABCD,他们的维数分别是: A = 50 ∗ 10 , B = 10 ∗ 40 , C = 40 ∗ 30 , D = 30 ∗ 5 A=50*10,B=10*40,C=40*30,D=30*5 A=5010B=1040C=4030D=305,则连乘 A , B , C , D A,B,C,D ABCD共有五种相乘的次序,如:

  1. ( A ( ( B C ) D ) ) (A((BC)D)) (A((BC)D)),数乘次数为 16000
  2. ( ( A B ) ( C D ) ) ((AB)(CD)) ((AB)(CD)),数乘次数为 36000
  3. ( ( A ( B C ) ) D ) ((A(BC))D) ((A(BC))D),数乘次数为 34500
  4. ( A ( B ( C D ) ) ) (A(B(CD))) (A(B(CD))),数乘次数为 10500
  5. ( ( ( A B ) C ) D ) (((AB)C)D) (((AB)C)D),数乘次数为 87500

我们可以看到,连乘的次序不同,它的数乘次数大区别也是很大的,最少的 10500 次,最多的,87500 次,相差 8 倍之多。所以,寻找一个矩阵连乘的最优分隔方式就显得尤为重要。


解题思路:
  这个问题可以使用枚举法进行解决,枚举每一个分隔方式的数乘次数,找到一个最小的即可,但是这样的算法的是时间复杂度就会很大,并不是一个合格的算法,为此,我们可以通过动态规划来解决该问题。
  将矩阵连乘 A i ∗ A i + 1 … … A j A_i*A_{i+1}……A_j AiAi+1Aj 简记为 A [ i : j ] A[i:j] A[i:j]
  1. 设定一个二维数组 m [ N ] [ N ] m[N][N] m[N][N],表示当前矩阵的连乘次数。比如: m [ i ] [ j ] m[i][j] m[i][j] 表示矩阵从 A i A_i Ai 连乘到 A j A_j Aj 的数乘值。
  2. 再设定一个二维数组 S [ N ] [ N ] S[N][N] S[N][N],表示当前矩阵连乘时的具有最优解的分隔位置。比如 s [ i ] [ j ] s[i][j] s[i][j] 表示从矩阵从 A i A_i Ai 连乘到 A j A_j Aj 的数乘值最小时的分隔位置,可以理解为加括号的位置。

给出递归关系:
m [ i ] [ j ] = { 0 , i==j m i n { m [ i ] [ k ] + m [ k + 1 ] [ j ] + A i − 1 ∗ A k ∗ A j } , im[i][j]={ 0,min{ m[i][k]+m[k+1][j]+Ai1AkAj},i==ji

核心算法详解:

  1. 先将m数组的对角线初始化为0,因为 m [ i ] [ i ] m[i][i] m[i][i] 表示只有一个矩阵,那它的数乘次数当然是0了。
  2. 依次计算从第二个矩阵连乘到最后一个矩阵的最优解情况。
    1. 依次在 r − 1 r-1 r1 个分隔位置中依次检测最优分隔点。
    2. 对于每个分隔点,变换一次分隔位置,再进行逐一测试,如果有更优的分隔点,就替换掉当前的分隔点。

矩阵连乘实例:

连乘矩阵 : { A 1 , A 2 , A 3 , A 4 , A 5 , A 6 } \{A_1,A_2,A_3,A_4,A_5,A_6\} { A1,A2,A3,A4,A5,A6}
在这里插入图片描述
m数组:
超详细的动态规划解决矩阵连乘_第2张图片
s数组:
超详细的动态规划解决矩阵连乘_第3张图片

#include
using namespace std;
const int N = 100;
int A[N];//矩阵规模
int m[N][N];//最优解
int s[N][N];
void MatrixChain(int n)
{
     
	int r, i, j, k;
	for (i = 0; i <= n; i++)//初始化对角线
	{
     
		m[i][i] = 0;
	}
	for (r = 2; r <= n; r++)//r个矩阵连乘
	{
     
		for (i = 1; i <= n - r + 1; i++)//r个矩阵的r-1个空隙中依次测试最优点
		{
     
			j = i + r - 1;
			m[i][j] = m[i][i]+m[i + 1][j] + A[i - 1] * A[i] * A[j];
			s[i][j] = i;
			for (k = i + 1; k < j; k++)//变换分隔位置,逐一测试
			{
     
				int t = m[i][k] + m[k + 1][j] + A[i - 1] * A[k] * A[j];
				if (t < m[i][j])//如果变换后的位置更优,则替换原来的分隔方法。
				{
     
					m[i][j] = t;
					s[i][j] = k;
				}
			}
		}
	}
}
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);//递归1到s[1][j]
	cout << ")";
}
int main()
{
     
	int n;//n个矩阵
	cin >> n;
	int i, j;
	for (i = 0; i <= n; i++)
	{
     
		cin >> A[i];
	}
	MatrixChain(n);
	cout << "最佳添加括号的方式为:";
	print(1, n);
	cout << "\n最小计算量的值为:" << m[1][n] << endl;
	return 0;
}

你可能感兴趣的:(算法,算法,动态规划)