动态规划——矩阵连乘积问题(个人学习用)

一、设计动态规划算法的步骤

(1).找出最优解的性质

(2).动态规划方程

(3).自底向上计算最优值

二、矩阵连乘积问题

计算三个矩阵ABC的成绩,由于矩阵乘法的性质,不同计算顺序导致的乘法运算量可能相差悬殊。即(AB)C 和A(BC)的运算量差距很大。

A:行数*列数 50*10 B:10*40 C:40*30

乘法运算次数:

(AB)C=(50*10*40)+50*40*30 =80000

A(BC)=50*10*30+(10*40*30)=27000

第二种计算方法的运算量是明显小于第一种的

三、分析最优解的结构

使用A[i,j]代表Ai,Ai+1...Aj的连乘积,做一个断点k,i<=k<j,k将矩阵的连乘积分割为两部分,Ai,Ai+1...Ak,Ak+1...Aj。以此,对乘积Ai,Ai+1...Aj的完全加括号的代价就是计算Ai,Ai+1...Ak,Ak+1...Aj的代价之和,再加上两者相乘的代价。

即:(Ai,Ai+1...Ak)+(Ak+1...Aj)+pi-1pkpj

Ai的维数为 Ai-1*temp Ai-1的原因是p[i]从0开始存储

//p[i]为行数,temp为列数
for(i=0;i<=n-1;i++)
		scanf("%d%d",&p[i],&temp);

若Ai,Ai+1...Aj的一个最优完全加括号方式是以k为断点,分割矩阵,则Ai,Ai+1...Ak的子链必须是它的一个最优完全加括号方式。反之,若存在Ai,Ai+1...Ak的一个代价更小的最优完全加括号方式,则它替换到Ai,Ai+1...Aj中则会产生另一个最优解。这样一来,任何最优解都包含子问题的最优解。

四、建立递归关系

A[i,j]代表Ai,Ai+1...Aj的连乘积,m[i][j]代表A[i][j]的最少次数。

A[1,n]代表A1,A2...An的连乘积,m[1][n]代表A[1,n]的最优解。

i,起始位置,j,终止位置。

当i=j,代表矩阵链只包含一个矩阵,A[1,1]代表矩阵链中只包含矩阵A1。

则m[i][i]=0.

i<j,在Ai,Ai+1...Aj中加一个断点k,i<=k<j,则m[i][j]等于子乘积A[i,k]和A[k+1,j]的和在加上两个矩阵相乘的乘法运算次数(代价),乘法运算次数就是m*n 矩阵A * n*p 矩阵B = m *p 矩阵C

图片来源:

https://www.cnblogs.com/beatrice7/p/4149639.html

定义数组s[i][j]保存k的值。

#include<bits/stdc++.h>
using namespace std;
#define NUM 51
int p[NUM];//行列数p[i-1]行数,p[i]为列数
int m[NUM][NUM];//保存最优解
int s[NUM][NUM];//保存断点k的值
int LookupChain(int i,int j)
{
	if(m[i][j]>0)return m[i][j];//m[i][j]存了子问题的运算结果
	if(i==j)return 0;//只含一个矩阵的情况。
	int u = LookupChain(i,i)+LookupChain(i+1,j)+p[i-1]*p[i]*p[j];//递推公式,以i为第一个断点
	s[i][j] = i;
	for(int k=i+1;k<j;k++)//从i后找不同断点的情况,保存最小值为最优解m[i][j]
	{
		int t = LookupChain(i,k)+LookupChain(k+1,j)+p[i-1]*p[k]*p[j];
		if(t<u){u=t;s[i][j]=k;}
	}
	m[i][j]=u;
	return u;
}
矩阵A1A2A3A4

A5

A6
行列数50*1010*4040*3030*55*2020*15
下标0123456
5010403052015

 

 五、构建最优解

(A[i,k])(A[k+1,j]),令s[i][j]=k

则A[1,n]的最优加括号方式为(A([1,s[1][n]))(A(s[1][n]+1,n))

void TraceBack(int i,int j)
{
	if(i==j)printf("A%d",i);
	else
	{
		printf("(");
		TraceBack(i,s[i][j]);
		TraceBack(s[i][j]+1,j);
		printf(")");
	}
}

完整代码为:

#include<bits/stdc++.h>
using namespace std;
#define NUM 51
int p[NUM];
int m[NUM][NUM];
int s[NUM][NUM];
int LookupChain(int i,int j)
{
	if(m[i][j]>0)return m[i][j];
	if(i==j)return 0;
	int u = LookupChain(i,i)+LookupChain(i+1,j)+p[i-1]*p[i]*p[j];
	s[i][j] = i;
	for(int k=i+1;k<j;k++)
	{
		int t = LookupChain(i,k)+LookupChain(k+1,j)+p[i-1]*p[k]*p[j];
		if(t<u){u=t;s[i][j]=k;}
	}
	m[i][j]=u;
	return u;
}
void TraceBack(int i,int j)
{
	if(i==j)printf("A%d",i);
	else
	{
		printf("(");
		TraceBack(i,s[i][j]);
		TraceBack(s[i][j]+1,j);
		printf(")");
	}
}
int main()
{
	int n;
	scanf("%d",&n);
	int i,temp;
	for(i=0;i<=n-1;i++)
		scanf("%d%d",&p[i],&temp);
	p[n]=temp;
	memset(m,0,sizeof(m));
	LookupChain(1,n);
	printf("%d\n",m[1][n]);
	TraceBack(1,n);
	return 0;
}
/*
input:
6
50 10
10 40
40 30
30 5
5 20
20 15
output:
15750
((A1(A2(A3A4)))(A5A6))
*/

本博客资料、代码来源于清华大学出版社算法设计与分析,本博客仅用于个人学习,可能存在纰漏,敬请批评指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值