Fork me on GitHub

矩阵链相乘助教版代码

题目描述

零崎有很多朋友,其中有一个叫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("\n");
41     }
42 }

 

posted @ 2015-11-19 20:14  伊甸一点  阅读(516)  评论(0编辑  收藏  举报