动态规划法是一种求最优解的数学思路,广泛应用于组合优化、图形解析等问题的相关算法中。将算式的计算结果记录在内存之中,需要时直接调用该结果,从而避免无用的重复计算,提高效率。
本文利用多维数组->进行动态规划法->完成最长公共子序列、矩阵列乘法问题。
引入:斐波那契数列
满足如下递推式子:
易得:
int fibonacci(int n){
if(n==0||n==1)
return 1;
return fibonacci(n-1)+fibonacci(n-2);
}
显然,这种充满缺陷,在每次重新计算时,都会重新计算全面的全部数据(造成可以避免的浪费)。
修改部分操作,将计算过的部分存入F[n]数组中,借此来避免重复计算。这种思路基于动态规划的基本结构。
通过记忆化递归生成斐波那契数列:
int fibonacci(int n){
if(n==0||n==1)
return F[n]=1;
if(F[n]!=-1)//已经存在了
return F[n];
return F[n]=fibonacci(n-1)+fibonacci(n-2);
}
利用动态规划法来实现如下:
void makeFibonacci(int F[],int n){
for(int i=0;i<=n;i++){
if(i<=1)
F[i]=1;
else
F[i]=F[i-1]+F[i-2];
}
}
最长公共子序列
(Longest Common Subsequence,LCS)
给定两个定序列X,Y,求两序列公共拥有的序列Z。设X={a,b,c,b,d,a,b},
Y={b,d,c,a,b,a},那么序列{b,c,a}就是X,Y的公共子序列,但并不是最长的,因为还存在序列{b,c,b,a}。
C[m+1][n+1];//表示X前i个和Y前j个的最长子序列
C数组的值可由下述的递推公式求得:
static const int N=1000
int lcs(string X,string Y){
int C[N+1][N+1];
int m=X.size();
int n=Y.size();
X=" "+X;
Y=" "+Y;
for(int i=1;i<=m;i++) C[i][0]=0;
for(int i=1;i<=n;i++) C[0][i]=0;
int maxv=0;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(X[i]==Y[j])
C[i][j]=C[i-1][j-1]+1;
else
C[i][j]=max(C[i-1][j],C[i][j-1]);
maxv=max(maxv,C[i][j]);
}
}
return maxv;
}
矩阵链乘法
(Matrix Chain Multiplication):一系列矩阵相乘求其最小次数的乘法操作的值
l*m的矩阵A与m*n的矩阵B相乘后得到l*n的矩阵C,需要完成的乘法的次数为l*m*n,C的各元素满足如下:
本题的目的是尽量减少计算过程中的乘法运算,不在意的值。
需要计算矩阵链,矩阵乘法满足结合率,导致有多种求法,从中用动态规划求最小的。
(M1)(M2M3M4)=M1+M2M3M4+p0*p1*p4
(M1M2)(M3M4)=(M1M2)+(M3M4)+p0*p2*p4...。
m[n+1][n+1];//矩阵i到矩阵j的成本
p[n+1];//Mi是p[i-1]*p[i]的矩阵
满足此递推公式:。
为了偷懒直接放代码了:
#include<iostream>
#include<algorithm>
using namespace std;
static const int N = 100;
int main(){
int n,p[N+1],m[N+1][N+1];
cin>>n;
for(int i=1;i<=n;i++){
cin>>p[i-1]>>p[i];
}
//单独矩阵0成本
for(int i=1;i<=n;i++)
m[i][i]=0;
for(int l=2;l<=n;l++){//进行操作的矩阵数量
for(int i=1;i<=n;i++){//起始矩阵编号
int j=i+l-1;//结束矩阵编号
m[i][j]=(1<<21);//为取最小值赋值最大
for(int k=i;k<=j-1;k++){
//*递推式
m[i][j]=min(m[i][j],m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]);
}
}
}
cout<<m[1][n]<<endl;
return 0;
}
输入
6
30 35
35 15
15 5
5 10
10 20
20 25
输出
15125