• 矩阵链相乘助教版代码


    题目描述

    零崎有很多朋友,其中有一个叫jhljx。

    jhljx大家很熟悉了,他数学不好也是出了名的,大家都懂。

    现在jhljx遇到了矩阵乘法,他当时就懵了。数都数不清的他,矩阵乘法怎么可能会算的清楚呢?虽然零崎觉得还不如让你们来算,不过好歹也要给jhljx个面子,给她留下一个证明自己数学实力的机会。为了减小jhljx的计算量,让他赶快算出不正确答案来(估计他算上几遍都不一定能出一个正确答案),零崎请你们帮助jhljx。

    输入

    多组输入数据。

    每组数据以N开始,表示矩阵链的长度。接下来一行N+1个数表示矩阵的行/列数。

    1<=N<=300

    输出

    对于每组样例,输出一行最少运算次数的方案,每两个矩阵相乘都用“()”括起来,详见样例

    如果存在多种解决方案,最终输出结果选取先计算左边的矩阵,详见Hint

    输入样例

    3
    10 30 5 60
    3
    10 20 5 4

    输出样例

    ((A1A2)A3)
    ((A1A2)A3)

    Hint

    对于输入的第二组数据,

    如果计算顺序为((A1A2)A3),结果为10×20×5 + 10×5×4= 1200,

    如果计算顺序为A1(A2A3), 结果为20×5×4 + 10×20×4 = 1200

    那么输出结果选取第一个

    解题思路:

    1、课堂上讲过的经典动态规划题:矩阵链乘问题(MCM)

    给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2…,n-1。如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。

    解答:我们按照动态规划的几个步骤来分析:

    (1)找出最优解的性质,刻画其特征结构

    对于矩阵连乘问题,最优解就是找到一种计算顺序,使得计算次数最少。

    令m[i][j]表示第i个矩阵至第j个矩阵这段的最优解。

    将矩阵连乘积 简记为A[i:j] ,这里i<=j.假设这个最优解在第k处断开,i<=k<j,则A[i:j]是最优的,那么A[i,k]和A[k+1:j]也是相应矩阵连乘的最优解。可以用反证法证明之。 这就是最优子结构,也是用动态规划法解题的重要特征之一。

    (2)建立递归关系

    假设计算A[i:j],1≤i≤j≤n,所需要的最少数乘次数m[i,j],则原问题的最优值为m[1,n] 。

    当i=j时,A[i,j]=Ai, m[i,j]=0;(表示仅一个矩阵,如A1,没有和其他矩阵相乘,故乘的次数为0)

    当i<j时,m[i,j]=min{m[i,k]+m[k+1,j] +pi-1*pk*pj} ,其中 i<=k<j

    (相当于对i~j这段,把它分成2段,看哪种分法乘的次数最少,如A1,A2,A3,A4,则有3种分法:{A1}{A2A3A4 }、{A1A2}{A3A4 }、{A1A2A3}{A4 },其中{}表示其内部是最优解,如{A1A2A3}表示是A1A2A3的最优解),

    也即(k为分隔点):

    (3)计算最优值

    对于1≤i≤j≤n不同的有序对(i,j) 对于不同的子问题,因此不同子问题的个数最多只有o(n*n)

    但是若采用递归求解的话,许多子问题将被重复求解,所以子问题被重复求解,这也是适合

    用动态规划法解题的主要特征之一,这也是为什么很多人RE的原因,递归过多导致爆函数栈。

    用动态规划算法解此问题,可依据其递归式以自底向上的方式进行计算。在计算过程中,保

    存已解决的子问题答案。每个子问题只计算一次,而在后面需要时只要简单查一下,从而避

    免大量的重复计算,最终得到多项式时间的算法。

    呈上代码:

     1 #include <cstdio>
     2 #include <cstring>
     3 long long m[311][311];
     4 long long p[311];
     5 int s[311][311];
     6 #define INF 9999999999
     7 void print(int i,int j)
     8 {
     9     if(i==j)
    10         printf("A%d",i);
    11     else{
    12         printf("(");
    13         print(i,s[i][j]);
    14         print(s[i][j]+1,j);
    15         printf(")");
    16     }
    17 }
    18 int main() {
    19     long long n,t;
    20     int j;
    21     while(~scanf("%lld",&n)){
    22         for(int i=1;i<=n;i++)
    23             m[i][i]=0;
    24         for(int i=0;i<=n;i++)
    25             scanf("%lld",&p[i]);
    26         for(int l=2;l<=n;l++)
    27         for(int i=1;i<=n-l+1;i++){
    28             j = i+l-1;
    29             m[i][j]=INF;
    30             for(int k=i;k<=j-1;k++){
    31                 t = m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
    32                 if(t<=m[i][j])
    33                 {
    34                     m[i][j]=t;
    35                     s[i][j]=k;
    36                 }
    37             }
    38         }
    39         print(1,n);
    40         printf("
    ");
    41     }
    42 }
  • 相关阅读:
    linux-指令
    rabbitmq启动
    [浪峰前端开发]JS获取当前时间戳的方法
    [浪峰JQuery开发]jquery最有意思的IFrame类似应用--值得深入研究
    [浪峰分享]移动电商:不是渠道拓展,而是一次重新创业
    [浪峰分享]App必死 Web永生 看Web的前世今生 必会卷土重来
    [浪峰分享]推荐一些不错的计算机书籍
    [浪峰转载]Jquery取得iframe中元素的几种方法
    [浪峰分享] 如何管理一个远程团队
    [浪峰分享] 博客园博客导航固顶--简单实用的css代码
  • 原文地址:https://www.cnblogs.com/zpfbuaa/p/4978932.html
Copyright © 2020-2023  润新知