动态规划③——矩阵链连乘!!!

        题前吐槽:已经好几天没更新了=_=,这几天的事也是多。。。课程设计数据结构大作业终于也是告一段落了,前几天忙于准备答辩PPT(以及由于报告写的太烂,而被老师批了,让我重新写.....),所以以后有时间会开一个数据结构大作业专栏,SCUT的同学可以关注一下哦,专题是科学文献管理系统,有相关作业的同学也可以码一下,到时会分享具体实现思路,以及报告要注意什么地方(亲身经历!!!)  此外,学校的srp项目也是越来越赶了,从原来的一个月开一次会,到后面三周开一次,到现在两周开一次。。。。只能牺牲一点娱乐时间看那些晦涩难懂的洋文章,写一些自己做完就忘了的(答辩)PPT,以及永远调不通的Python代码(这个是真难绷),后面也会开一个srp论文阅读笔记以及学习心得,主要防止自己刚读完的内容第二天就忘了!有类似项目的同学也可以码一下~(后面还有好多实验,计网,数据库,操作系统各种实验扎堆来,虽然课少了,但感觉事也很多啊=_=,有空会更新~)

        OK!闲话说完了,现在进入正题。好几天没看,发现前面两篇的动态规划竟然阅读量还不错(对我来说),也有那么几个人收藏了。今天也继续探索一下动态规划中的一个经典问题--区间DP,具体释义看区间DP,为什么突然就跳到这题了呢?主要是因为算法老师说期末考试有一半概率考到(迫于无奈╮(╯▽╰)╭)。

        首先来看一下题目!更加具体的矩阵怎么相乘的csdn上其他文章也有很详细的解释:矩阵连乘。我这里就假定大家都知道矩阵相乘的定义,题目即是要让一堆很长的矩阵相乘,然后求出一种分割方法使得计算次数最少。当然我们知道矩阵的相乘顺序会影响它的计算次数。举个例子:假定矩阵A1:3*4 A2:4*5 A3:5*6 ,如果A1先和A2相乘再和A3相乘则计算总次数为3*4*5+3*5*6=150次,若A2先和A3相乘再和A1相乘则计算总次数为4*5*6+3*4*6=192次!差异还是很明显的!

        虽然很神奇,但不纠结于其数学特性来谈的话,它跟区间DP的定义还是十分吻合的。即分阶段划分问题,合并的代价为所有子区间各自的代价和加上将两个子区间合并的代价。即

        求最大值还是最小值根据题目要求来。那么这个矩阵链连乘问题怎么入手呢?我们还是根据动态规划的步骤:①首先确定dp数组的状态的含义:此处的dp[i][j]可以表示为第i个矩阵到第j个矩阵相乘所需的最小次数;②其次确定状态转移方程:

这里i=j的时候表示,只有自己一个矩阵当然不需要相乘次数。若i<j,那么我们就要考虑在矩阵链的哪个地方断开,分成更小的子问题,最后别忘了加上合并子问题的代价。重点是怎么求这个k(断开的位置呢?)。方法很简单:全部可能的位置遍历一遍就行了(当然应该有更好的优化方法)。因为当我们确定好遍历的顺序后,m[i,k]和m[k+1,j]的值我们保存在dp数组中,也就减少了重复计算。

        最后我们来看一下,如何确定遍历的顺序。这道题目比较特殊,由于矩阵的相乘是按固定顺序的(例如只能从1-n,而不会从n-1),也就是说这个遍历的dp数组(二维表),只需要右上角那一部分(去除类似[2][1],[3][2]这样的逆序遍历)。网上没有找到具体的oj题,这里简单实现一下具体的代码,了解一下思想。至此我们可以写出最终的代码如下:

#include<iostream>
using namespace std;

const int N = 100;
int dp[N+1][N+1];
int index[N + 1][N + 1];//记录断开的位置
int p[N+1];//这里代表矩阵的维度,即行数和列数,A1由p0和p1组成,以此类推

int main() {
	int n;
	cout << "输入矩阵个数:" << endl;
	cin >> n;//n代表矩阵的个数

	cout << "按顺序输入矩阵维度:" << endl;
	for (int i = 0; i <= n; i++) {
		cin >> p[i];
	}

	for (int r = 2; r <= n; r++) {//对角线循环,r表示每一行的矩阵长度逐渐变长
		for (int i = 1; i <= n - r + 1; i++) {//行循环,r为1时遍历n行(此时为特殊情况,全局变量已置0),r为2时遍历n-1行,由此推出
		
			int j = r + i - 1;//列循环,当r为1时从第i列开始遍历,当r为2时从第i+1列开始遍历,由此推出
		
			//由于全局变量初始化为0了,所以先给它赋个初值再进行比较:
			dp[i][j] = dp[i + 1][j] + p[i - 1] * p[i] * p[j];//此处是k=i的情况,即在i处断开
			index[i][j] = i;
			
			//下面为状态转移方程,加上k循环:
			for (int k = i+1; k <= j - 1; k++) {
				int temp = dp[i][k] + dp[k + 1][j] + p[i - 1] * p[k] * p[j];
				if (temp < dp[i][j]) {
					dp[i][j] = temp;
					index[i][j] = k;
				}
			}		
		}	
	}

	cout <<"最终结果为:"<< dp[1][n] << endl;//输出最后结果

}

最后结果大家可以去试试(第一次写这么长的文,好累=_=),有什么问题可以评论区讨论~

        

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值