矩阵连乘的问题

/*
  Name: 
  Copyright: 
  Author: 
  Date: 27-03-17 08:14
  Description: 动态规划——矩阵连乘的问题
《问题的引出》
看下面一个例子,计算三个矩阵连乘{A1,A2,A3};维数分别为10*100 , 100*5 , 5*50
按此顺序计算需要的次数((A1*A2)*A3):10X100X5+10X5X50=7500次
按此顺序计算需要的次数(A1*(A2*A3)):10X5X50+10X100X50=75000次
所以问题是:如何确定运算顺序,可以使计算量达到最小化。
枚举显然不可,如果枚举的话,相当于一个“完全加括号问题”,次数为卡特兰数,卡特兰数指数增长,必然不行。

《建立递归关系》
子问题状态的建模(很关键):令m[i][j]表示第i个矩阵至第j个矩阵这段的最优解。
显然如果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 ;
代码实现时需要注意的问题:计算顺序!!!
因为你要保证在计算m[i][j]查找m[i][k]和m[k+1][j]的时候,m[i][k]和m[k+1][j]已经计算出来了。
*/
#include<iostream>
#include<string>

using namespace std;

int matrixChain(int i, int j);//自顶向下,使用备忘录数组的动态规划算法  
int matrixChain_2(int n);//自底向上的动态规划算法  
void traceback(int i,int j);//根据s[][]记录的各个子段的最优解,将其输出

const int MAX = 100;
//p用来记录矩阵的行列,p[i-1]和p[i]分别记录了矩阵A[i]的行数和列数 
//m[i][j]用来记录第i个矩阵至第j个矩阵的最优解
//s[][]用来记录从哪里断开的才可得到该最优解
int p[MAX+1],m[MAX+1][MAX+1],s[MAX+1][MAX+1];
int flag[MAX+1]; //记录A[i]是否已经被输出过 

int main(int argc, char **argv)
{
 	//测试数据可以设为六个矩阵分别为
	//A1[30*35],A2[35*15],A3[15*5],A4[5*10],A5[10*20],A6[20*25]
	//则p[6]={30,35,15,5,10,20,25}
	int n = 6;
    int t[MAX+1] = {30,35,15,5,10,20,25};
    for (int i=0; i<=n; i++)
    	p[i] = t[i];
    
    cout << matrixChain(1, n) << endl;
 //   cout << matrixChain_2(n) << endl;
    
    traceback(1, n);
    cout << endl;
    
    system("pause");
    return 0;
}

int matrixChain(int i, int j)//自顶向下,使用备忘录数组的动态规划算法  
{
 	if (m[i][j] != 0) //默认为0
        return m[i][j];
	if (i == j)
		return 0;
	//先处理k=i的情形		
	m[i][j] = matrixChain(i+1, j) + p[i-1] * p[i] * p[j]; //matrixChain(i, i)==0,就不写了 
	s[i][j] = i;
	//再处理i<k<j的情形
	for (int k=i+1; k<j; k++)
	{
	 	int t = matrixChain(i, k) + matrixChain(k+1, j) + p[i-1] * p[k] * p[j];
	 	if (t < m[i][j])
	 	{
	        m[i][j] = t;
	        s[i][j] = k;
        }
	} 
	return m[i][j];
}

int matrixChain_2(int n)//自底向上的动态规划算法  
{
 	for (int i=1; i<=n; i++)
 		m[i][i] = 0;
    
	for (int len=2; len<=n; len++)//括号里矩阵的数量(长度)
	{
	 	for (int i=1; i<=n-len+1; i++)
	 	{
		 	int j = i + len -1;
		 	//先处理k=i的情形
		 	m[i][j] = m[i][i] + m[i+1][j] + p[i-1]*p[i]*p[j];
		 	s[i][j] = i;
		 	//再处理i<k<j的情形
		 	for (int k=i+1; k<j; k++)
			{
			 	int t = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j];
			 	if (t < m[i][j])
			 	{
			        m[i][j] = t;
			        s[i][j] = k;
		        }
			} 
		}
	}
	
	return m[1][n];
}

void traceback(int i,int j)//根据s[][]记录的各个子段的最优解,将其输出
{
    if (i == j)
	   return ;
    
    cout << "(";
    traceback(i, s[i][j]);
    if (flag[i] == 0)
    {
        cout << "A" << i;
        flag[i] = 1;
    }
    traceback(s[i][j]+1, j);
    if (flag[j] == 0)
    {
        cout << "A" << j;
        flag[j] = 1;
    }
    cout << ")";
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值