动态规划&矩阵连乘
动态规划的概念
• 与分治方法类似
分-治-合
• 与分治方法不同
子问题之间并非相互独立
• 基本思想
用一个表记录所有子问题的解,不管子问题是否被用到,只要它被计算过,就将其结果备份至表中
动态规划的基本要素
• 最优子结构
利用问题的最优子结构性质,以自底向上的方式递归地从子问题的最优解逐步构造出整个问题的最优解。最优子结构是问题能用动态规划算法求解的前提。
• 重叠子问题
• 递归算法求解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。这种性质称为子问题的重叠性质。
• 动态规划算法,对每一个子问题只解一次,而后将其解保存在一个表格中,当再次需要解此子问题时,只是简单地用常数时间查看一下结果。
• 通常不同的子问题个数随问题的大小呈多项式增长。因此用动态规划算法只需要多项式时间,从而获得较高的解题效率。
动态规划的步骤
• 找出最优解的性质
• 递归地定义最优值
• 以自底向上的方式计算出最优值
• 根据计算获得的最优值,构造最优解
矩阵连乘
• 问题
给定n个矩阵(A1,A2,…,An),其中Ai和Ai+1是可乘的,求解这n个矩阵的连乘积A?
• 矩阵连乘积A=A1A2A3A4
• 计算次序
(((A1A2)A3)A4))
((A1(A2A3))A4)
(A1((A2A3)A4))
(A1(A2(A3A4)))
((A1A2)(A3A4))
• 最优子结构性质
原问题的最优解包含其子问题的最优解
A[1:n]的最优计算次序所包含的计算矩阵子链A[1:k]和A[k+1:n]也是最优的
数学归纳法可以证明
• 建立递归关系
令A[i:j]所需的最少数乘次数为m[i][j].
当i=j时,m[i][j]=0
当i<j时,m[i][j]=m[i][k]+m[k+1][j] +pi-1*pk*pj
构造递归关系
将对应的m[i][j]的断开位置k记为s[i][j],可递归的构造最优解
• 递归求解
int matrixChainRecur(int i, int j){
if (i == j) return ;
int u = matrixChainRecur(i, i) + matrixChainRecur(i+, j)
+ p[i-] * p[i] * p[j];
s[i][j] = i;
for (int k = i + ; k < j; k++) {
int t = matrixChainRecur(i, k) + matrixChainRecur(k+, j)
+ p[i-] * p[k] * p[j];
if (t < u){
u = t;
s[i][j] = k;
}
}
return u;
}
• 在递归计算时,许多子问题被重复计算多次。这也是该问题可用动态规划算法求解的又一显著特征。
• 用动态规划算法解此问题,可依据其递归式以自底向上的方式进行计算。在计算过程中,保存已解决的子问题答案。每个子问题只计算一次,而在后面需要时只要简单查一下,从而避免大量的重复计算,最终得到多项式时间的算法
• 计算最优值
A1 |
A2 |
A3 |
A4 |
A5 |
A6 |
30´35 |
35´15 |
15´5 |
5´10 |
10´20 |
20´25 |
# include<stdio.h>
# include<iostream>
using namespace std; int m[][],s[][],n,p[];
void matrixChain()
{
int i,j,k,r;
for(i=; i<=n; i++) m[i][i]=;
for(r=; r<=n; r++) //对角线
for( i=; i<=n-r+; i++) //行
{
j = r+i-; //列
m[i][j]=m[i][i]+m[i+][j]+p[i-]*p[i]*p[j];
s[i][j]=i;
for(k = i+; k<j; k++)
{
int temp=m[i][k]+m[k+][j]+p[i-]*p[k]*p[j];
if(temp<m[i][j])
{
m[i][j]=temp;
s[i][j]=k;
}
}
}
} void traceback(int i,int j)
{
if(i==j)return ;
traceback(i,s[i][j]);
traceback(s[i][j]+,j);
cout<<"Multiply A"<<i<<","<<s[i][j]<<"and A"<<s[i][j]+<<","<<j<<endl;
} int main()
{
cin>>n;
for(int i=; i<=n; i++)
cin>>p[i];
//测试数据可以设为六个矩阵分别为
//A1[30*35],A2[35*15],A3[15*5],A4[5*10],A5[10*20],A6[20*25]
//则p[0-6]={30,35,15,5,10,20,25}
//输入:30 35 15 5 10 20 25
matrixChain();
traceback(,n);
//最终解值为m[1][n];
cout<<m[][n]<<endl;
return ;
}