矩阵连乘问题(动态规划)

19 篇文章 0 订阅

计算三个矩阵连乘{A1,A2,A3},维数分别为10*100 , 100*5 , 5*50

按此顺序计算需要的次数((A1*A2)*A3):10X100X5+10X5X50=7500次

按此顺序计算需要的次数(A1*(A2*A3)):10X5X50+10X100X50=75000次

所以问题是:如何确定运算顺序,可以使计算量达到最小化。枚举显然不可,如果枚举的话,相当于一个“完全加括号问题”,次数为卡特兰数,卡特兰数指数增长,必然不行。

子问题状态的建模(很关键):

将AiAi+1...Aj简记为A[i:j],(0<=i<=j<=n-1),所需的最少数乘次数为m[i][j],则原问题的最优值为m[0][n-1] 

显然如果i=j,则m[i][j]这段中就一个矩阵,需要计算的次数为0; 

如果i>j,则m[i][j]=min{m[i][k]+m[k+1][j]+p[i-1]Xp[k]Xp[j]},其中k,在i与j之间游荡,所以i<=k<j ;

辅助数组s来记录计算m[i][j]时取得最优代价处的k的值。

m和s数组的计算顺序如图所示(只用到上三角矩阵):


#include<iostream>
#include<iomanip>
#define SIZE 6 //控制参与连乘矩阵的个数
using namespace std;
/*
* 数组m记录最少数乘次数,s记录断开的位置k 
* AiAi+1...Aj简记为A[i:j],(0<=i<=j<=n-1),所需的最少数乘次数为m[i][j],则原问题的最优值为m[0][n-1] 
*/
int m[SIZE][SIZE],s[SIZE][SIZE];
void MatrixChain(int p[],int n)
{
	for(int i=0;i<n;i++)//i=j时为单一矩阵,即矩阵链长度为1,无需计算
		m[i][i]=0;
    for(int l=2;l<=n;l++)//l为矩阵链长度,从2开始循环 
    {
		for(int i=0;i<n-l+1;i++)//l长度的矩阵链的第一个矩阵, 即A[i:j]中的i 
		{
			int j=i+l-1;//根据当前的长度算出矩阵链的最后一个矩阵
			m[i][j]=m[i+1][j]+p[i]*p[i+1]*p[j+1];
			s[i][j]=i+1;
			for(int k=i+1;k<j;k++)//遍历所有断开位置k
			{
				int min=m[i][k]+m[k+1][j]+p[i]*p[k+1]*p[j+1];//寻找最小值 
				if(min<m[i][j]) 
				{
					m[i][j]=min;
					s[i][j]=k+1;
				}
			}
		}
    }
}
void Traceback(int i,int j) //找出s数组中记录的最优断开点
{
	if(i==j)
		cout<<"A"<<i+1;
	else
	{
		cout<<"(";
		Traceback(i,s[i][j]-1);
		Traceback(s[i][j],j);
		cout<<")";
	}
}
int main()
{
	system("title 矩阵连乘问题");
	int p[SIZE+1]={30,35,15,5,10,20,25}; //p记录矩阵的行列数
	MatrixChain(p,SIZE);
	cout<<"输出m矩阵为:"<<endl;
	for(int i=0;i<SIZE;i++)
	{
		for(int j=0;j<SIZE;j++)
		{
			cout<<setw(5)<<m[i][j]<<" ";
		}
		cout<<endl;
	}
	cout<<endl<<endl;
	cout<<"输出s矩阵为:"<<endl;
	for(int i=0;i<SIZE;i++)
	{
		for(int j=0;j<SIZE;j++)
		{
			cout<<setw(5)<<s[i][j]<<" ";
		}
		cout<<endl;
	}
	cout<<"\n矩阵连乘加括号方式为:";
	Traceback(0,SIZE-1);
	return 0;
}


参考 http://www.cnblogs.com/liushang0419/archive/2011/04/27/2030970.html

参考《算法导论》 P197-201

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值