动态规划-矩阵连乘问题

矩阵连乘问题描述

  • 给定 n n n个矩阵 { A 1 , A 2 , … , A n } \left\{A_{1}, A_{2}, \ldots, A_{n}\right\} { A1,A2,,An},其中 A i A_{i} Ai A i + 1 A_{i+1} Ai+1是可乘的, i = 1 , 2 , 3 , . . . , n i=1,2,3,...,n i=1,2,3,...,n
  • 考查这 n n n个矩阵的连乘积 A 1 A 2 … A n A_{1}A_{2} \ldots A_{n} A1A2An
  • 由于矩阵乘法满足结合律,所以计算矩阵的连乘可以有许多不同的计算次序。这种计算次序可以用加括号的方式来确定
  • 若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则可以依此次序反复调用 2 2 2个矩阵相乘的标准算法计算出矩阵连乘积

算法描述

假设我们要计算6个矩阵的连乘积问题,如下:

从连乘矩阵个数为2开始计算每次的最小乘次数 m [ i ] [ j ] m[i][j] m[i][j](为便于理解,矩阵下标从1开始):

  • 连乘矩阵个数为2时:m[1][2]、m[2][3]、m[3][4]、m[4][5]、m[5][6]
  • 连乘矩阵个数为3时:m[1][3]、m[2][4]、m[3][5]、m[4][6]
  • 连乘矩阵个数为4时:m[1][4]、m[2][5]、m[3][6]
  • 连乘矩阵个数为5时:m[1][5]、m[2][6]
  • 连乘矩阵个数为6时:m[1][6] //要求最小乘次数
动态规划代码实现:
  • p i p_{i} pi:第 i i i个矩阵的行数、 p k p_{k} pk: i : k i:k i:k矩阵连乘后得到的列数, p j p_{j} pj: k + 1 : j k+1:j k+1:j矩阵连乘后得到的列数
  • m [ i ] [ j ] m[i][j] m[i][j]:最小乘次数数组, s [ i ] [ j ] s[i][j] s[i][j]:最优断开位置数组
    例如,在计算m[1][4]时,依据递归式有:
    m [ 1 ] [ 4 ] = min ⁡ { m [ 1 ] [ 1 ] + m [ 2 ] [ 4 ] + p 0 p 1 p 4 m [ 1 ] [ 2 ] + m [ 3 ] [ 4 ] + p 0 p 2 p 4 m [ 1 ] [ 3 ] + m [ 4 ] [ 4 ] + p 0 p 3 p 4 m[1][4]=\min \left\{\begin{array}{l}{m[1][1]+m[2][4]+p_{0} p_{1} p_{4}} \\ {m[1][2]+m[3][4]+p_{0} p_{2} p_{4}} \\ {m[1][3]+m[4][4]+p_{0} p_{3} p_{4}}\end{array}\right. m[1][4]=minm[1][1]+m[2][4]+p0p1p4m[1][2]+m[3][4]+p0p2p4m[1][3]+m[4][4]+p0p3p4
void matrixChain(int *p, int n, int m[][N], int s[][N])
{
   
	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++)
		{
   
			int j = i + r - 1; //j表示连乘矩阵中的最后一个
			// k == i时
			m[i][j] = m[i][i] + m[i + 1][j] + p[i - 1] * p[i] * p[j];
			s[i][j] = i;
			for (int k = i + 1; k < j; k++)
			{
   
				int tmp = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];
				if (tmp < m[i][j])
				{
   
					m[i][j] = tmp;
					s[i][j] = k;
				}
			}
		}
	}
}

递归构造最优解:
void traceBack(int i, int j, int s[][N])
{
   
	if (i == j)
		std::cout << "A" << i;
	else
	{
   
		std::cout << "(";
		traceBack(i, s[i][j], s);
		traceBack(s[i][j] + 1, j, s);
		std::cout << ")";
	}
}

测试结果:


对于矩阵连乘问题,常用的还有备忘录方法,与动态规划方法不同的是,备忘录方法是自顶向下的(动态规划方法是自底向上)。两种方法有相似之处,了解一个另一个也很好理解。

参考书:计算机算法设计与分析 第四版 王晓东著

全部评论

相关推荐

01-28 16:12
中南大学 Java
几年前还没有chatgpt的时候,刷题真的是很痛苦。刷不出来只能看题解,题解有几个问题:第一个是每次看的写题解的人都不一样,很难有一个统一的思路;第二个也是最重要的是,题解只提供了作者自己的思路,但是没有办法告诉你你的思路哪里错了。其实很少有错误的思路,我只是需要被引导到正确的思路上面去。所以传统题解学习起来非常困难,每次做不出来难受,找题解更难受。但是现在chatgpt能做很多!它可以这样帮助你&nbsp;-1.&nbsp;可以直接按照你喜欢的语言生成各种解法的题解和分析复杂度。2.&nbsp;把题和你写的代码都发给它,它可以告诉你&nbsp;你的思路到底哪里有问题。有时候我发现我和题解非常接近,只是有一点点🤏想错了。只要改这一点点就是最优解。信心倍增。3.&nbsp;如果遇到不懂的题解可以一行一行询问为什么要这样写,chatgpt不会嫌你烦。有时候我觉得自己的range写错了,其实那样写也没错,只是chat老师的题解有一点优化,这个它都会讲清楚。4.&nbsp;它可以帮你找可以用同类型解法来做的题。然后它可以保持解法思路不变,用一个思路爽刷一个类型的题。如果题目之间思路又有变化,它会告诉你只有哪里变了,其他的地方还是老思路。5.&nbsp;它也可以直接帮你总结模板,易错点。经过chat老师的指导,我最大的改变是敢刷题了。之前刷题需要先找某一个人写的算法题repo,然后跟着某一个人他的思路刷他给的几个题。如果想写别的题,套用思路失败了,没有他的题解,也不知道到底哪里错了;看别人的题解,思路又乱了。这个问题在二分查找和dp类型的题里面特别常见。但是现在有chat老师,他会针对我的代码告诉我我哪里想错了,应该怎么做;还按照我写代码的习惯帮我总结了一套属于我的刷题模板。每天写题全是正反馈!
牛客981:不刷才是爽
AI时代的工作 VS 传...
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务