• 动态规划之矩阵链相乘问题(算法导论)


    问题描述

    给定n个矩阵序列,(A1,A2,A3,A4,...,An). 计算他们的乘积:A1A2A3...An.

    由于矩阵的乘法运算符合结合律,因而可以通过调整计算顺序,从而降低计算量。


    样例分析

    比如有三个矩阵分别为:A1: 10*100,A2: 100*5,A3: 5*50

    假如现在按照(A1A2)A3的顺序计算需要的计算量为:10*100*5+10*5*50=7500次运算。

    若按照A1(A2A3)的顺序计算,需要的计算量为:100*5*50+10*100*50=75000次运算。

    上面两种不同的运算顺序所有的计算量相差十倍。

    因而,一种最优的计算顺序将能很大程度的减少矩阵连乘的运算量。


    问题解析

    此问题的目的是寻找一种最优的括号化方案。下面用动态规划的思想来进行分析:

    1、动态规划的第一步:寻找最优子结构。为方便起见,使用Ai..j表示AiAi+1...Aj的乘积结果矩阵。对于k(i<=k<j), 计算Ai..j所需要的计算量为:Ai..k 和 Ak+1..j 以及二者相乘的代价和。

    2、设m[i][j]为Ai..j的最优计算顺序所要花费的代价。则其求解公式为:

    if i == j, m[i][j] = 0; //因为只有一个矩阵时计算代码为0,即不需要计算。

    m[i][j]=min{m[i][k] + m[k+1][j] + Pi-1PkPj} i<=k<j

    3、为了能够输出求解顺序,需要保存区间中的一些分割点。假如Ai..j中的最优分割点为k,则我们使用s[i][j]=k。即在Ai..j中,分别计算Ai..k 和 Ak+1..j 所用的计算开销最小。

    4、采用自底向上的表格法。依次求解矩阵长度为2,3,...,n的最优计算顺序。


    算法思想

    1、对m[i][i]全部初始化为0.

    2、在矩阵链A1..n中,依次计算长度len为2,3,...,n的m[i][j]大小。(j-i+1==长度len).

    3、对于长度为len的m[i][j]初始化为+∞。然后根据以下公式计算m[i][j]的最小值。

    m[i][j]=min{ m[i][k] + m[k+1][j] + Pi-1PkPj }

    由于比长度len小的m[i][k],m[k+1][j]都已经提前计算了出来。所以就可以计算出最小的m[i][j],同时保存相应的最优点。如:s[i][j] = k; //k为i~j的最优计算分割点。

    4、根据以上保存的结果,输出。


    具体代码如下:(C代码)

    <span style="font-size:18px;">/**
    	动态规划之矩阵链相乘,
    	输入:有N个矩阵连乘,用一行有n+1个数数组表示,表示是n个矩阵的行及第n个矩阵的列,它们之间用空格隔开. 
    	输出:每组测试数据的输出占一行,它是计算出的矩阵最少连乘积次数,输出最优全括号结构
    	样例输入:10 100 5 50
        上面一组数据分别代表: A1:10*100, A2:100*5, A3:5*50
    	样例输出:7500 ((A1A2)A3)
    
    	30 35 15 5 10 20 25 --> 15125 ((A1(A2A3))((A4A5)A6))
    **/
    #include <stdio.h>
    int m[1002][1002],s[1002][1002];
    void matrix_chain(int a[], int n)
    {
    	int l, i, j, k, tmp;
    	for(l=2; l<=n; l++)
    	{
    		for(i=1; i<=n-l+1; i++)		//长度为l的区间,其最小下标为1~n-l+1
    		{
    			j=i+l-1;
    			m[i][j] = 0x7fffffff;
    			for(k=i; k<j; k++)		//i~k, k+1~j, 所以k<j
    			{
    				tmp = m[i][k]+m[k+1][j]+a[i-1]*a[k]*a[j];
    				if(tmp < m[i][j])
    				{
    					m[i][j] = tmp;
    					s[i][j] = k;
    				}
    			}
    		}
    	}
    
    }
    void print(int i, int j)
    {
    	if(i == j)
    		printf("A%d",i);
    	else{
    		printf("(");
    		print(i, s[i][j]);
    		print(s[i][j]+1, j);
    		printf(")");
    	}
    }
    int main()
    {
    	int n, a[1002];
    	int i,j,l;
    	while(scanf("%d",&n)==1)	//输入有n个矩阵
    	{
    		for(i=0; i<n+1; i++)
    			scanf("%d",&a[i]);
    		
    		//memset(m, 0x7fffffff,sizeof(m));
    		for(i=0; i<n+1; i++)
    			m[i][i] = 0;
    		matrix_chain(a, n);
    		printf("%d
    ",m[1][n]);
    		print(1, n);
    		printf("
    ");
    	}
    
    	return 0;
    }
    </span>





  • 相关阅读:
    Day-10: 错误、调试和测试
    Day-9: 面对对象高级编程
    json文件解析
    sqlite3入门之sqlite3_get_table,sqlite3_free_table
    sqlite3入门之sqlite3_open,sqlite3_exec,slite3_close
    字符集编码与字符大小
    让ubuntu下的eclipse支持GBK编码
    使用virtualbox安装unbuntu开启共享文件夹时遇到的权限问题
    QT--信号与槽
    QT--初识
  • 原文地址:https://www.cnblogs.com/liuwu265/p/4032128.html
Copyright © 2020-2023  润新知