算法和数据结构——动态规划法(Dynamic Programming,DP)

动态规划法是一种求最优解的数学思路,广泛应用于组合优化、图形解析等问题的相关算法中。将算式的计算结果记录在内存之中,需要时直接调用该结果,从而避免无用的重复计算,提高效率。

本文利用多维数组->进行动态规划法->完成最长公共子序列矩阵列乘法问题。


引入:斐波那契数列

满足如下递推式子:fib(n)=\left\{\begin{matrix}1(n=0)\\1(n=1)\\ fib(n-1)+fib(n-2) \end{matrix}\right.

易得:

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数组的值可由下述的递推公式求得:C[i][j]=\left\{\begin{matrix} 0(i=0orj=0) \\ C[i-1][j-1]+1 (i,j>0andx_{i}=y_{j}) \\max(C[i-1][j],C[i][j-1])(i,j>0andx_{i}\neq y_{j}) \end{matrix}\right.

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的各元素C_{ij}满足如下:

                                                                   C_{ij}=\sum_{k=1}^{m}a_{ik}*b_{kj}

本题的目的是尽量减少计算过程中的乘法运算,不在意C_{ij}的值。

需要计算矩阵链(M_{1}M_{2}M_{3}M_{4}),矩阵乘法满足结合率,导致有多种求法,从中用动态规划求最小的。

(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]的矩阵

满足此递推公式:m[i][j]=\left\{\begin{matrix} 0(i=j)\\min_{i\leq k\leq j}(m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j])(i\neq j) \end{matrix}\right.

为了偷懒直接放代码了:

#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

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值