题目描述
零崎有很多朋友,其中有一个叫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 }