动态规划问题总结

一.动态规划问题特征:

---最优子结构性质:最优解包含子问题最优解

---重叠子问题性质:求解过程会重复求解子问题

 

二.矩阵连乘问题

1.问题描述:

 

 

 

 

2.特征:最优子结构:假设连乘最优解在k处断开,则最优解的连乘方式也包括A[i,k-1]和A[k,j]的最优解

    重叠子问题:在计算不同的断开点所需的计算量时,会出现多次重复的计算

 

 3.解决方案:

①递归+备忘录算法:

a.建立递归方程:

 

即计算最优解断开处k的连乘次数,需要递归运算i~k,k+1~j最优解断开处的连乘次数。

 

b.备忘录:

内容:每次计算连乘次数后保留连乘的矩阵下标和连乘次数

作用:降低时间复杂度(降低前递归算法时间复杂度:O(2^n))

 

---算法分析:

时间复杂度:O(n^3)

空间复杂度:O(n^2)

 

---------------------------------------------------------具体代码+注释----------------------------------------------------------------

#include<bits/stdc++.h>

using namespace std;

 

int m[2000][2000];                //设置m数组,m[i][j]表示矩阵i~j的最小连乘次数

int s[2000][2000];                 //记录最优断点,s[i][j]表示矩阵i~j的最优解断点位置

int p[2000];                          //p[i-1]为矩阵i的行数;p[i]为Ai的列数

int n;                                   //矩阵个数

 

int RecurMatrixChain(int i, int j)

void print(int i, int j) ;

 

int main(){

  cin>>n;

  for(int i=1;i<=n;i++)                                           //设置下标1~n方便后续计算

    for(int j=1;j<=n;j++) m[i][j]=0;                     //m数组初始化

  cout<<RecurMatrixChain(1,n)<<endl;

  print(1,n)

  return 0;

}

 

int RecurMatrixChain(int i,int j){

  if(i==j) return 0;                                                         //i,j相等无意义

  if(m[i][j]>0) return m[i][j];                                           //m初始值为0,若m[i][j]>0,则为已经计算好的i~j最优解(备忘录)

  m[i][j]=RecurMatrixChain(i+1,j)+p[i-1]*p[i]*p[j];        //m[i][j]的初值为断点为i时的最少计算量

  s[i][j]=i;                                                                    //记录初始断点

  for(int k=i+1;k<j;k++){                                             //尝试后续断点,寻找最优解

    int t=RecurMatrixChain(i,k)+RecurMatrixChain(k+1,j)+p[i-1]*p[k]*p[j];

    if(t<m[i][j]) {m[i][j]=t;s[i][j]=k;}                           //如果计算量更小则刷新m和s数组

  }

  return m[i][j];

}

 

void print(int i,int j){                                                 //打印最优解方案

  if(i==j) cout<<"A"<<i;

  else{

    int k=s[i][j];               

    cout<<"(";

    print(i,k);                                                          //递归打印子问题中的矩阵和括号

    print(k+1,j);

    cout<<")";

  }

}

 

②填表法

 

 

 

 

 

 

a.填表分析:

表的维度:n*n------------------存放m,s数组

表的范围:上三角-------------i<j

填表顺序:对角线开始,自下而上,从左到右(如图)

最优解位置:右上角元素

 

 

 

 

---算法分析:

时间复杂度:O(n^3)

空间复杂度:O(n^2)

 

---------------------------------------------------------具体代码+注释----------------------------------------------------------------

#include<bits/stdc++.h>

using namespace std;

 

int m[2000][2000];                //设置m数组,m[i][j]表示矩阵i~j的最小连乘次数

int s[2000][2000];                 //记录最优断点,s[i][j]表示矩阵i~j的最优解断点位置

int p[2000];                          //p[i-1]为矩阵i的行数;p[i]为Ai的列数

int n;                                   //矩阵个数

 

int dp();

void print(int i,int j);

 

int main(){

  cin>>n;

  for(int i=1;i<=n;i++) cin>>p[i];

  cout<<dp()<<endl;

  print(1,n);

  return 0;

 

int dp(){

  for(int i=n;i>=1;i--){                                               //行:自下而上

    for(int j=i;j<=n;j++){                                       //列:对角线开始,从左到右

      if(i==j) m[i][j]=0;

      else{

       m[i][j]=m[i+1][j]+ p[i-1] * p[i] * p[j];                                                      //m[i][j]的初值为断点为i时的最少计算量

       s[i][j]=i;                                                                                                //记录断点

       for(int k=i+1;k<j;k++){                                                                         //尝试后续断点,寻找最优解

         int t=m[i][k] + m[k+1][j] + p[i-1] * p[k] * p[j];

         if(t<m[i][j]) {m[i][j]=t;s[i][j]=k;}                                                        //如果计算量更小则刷新m和s数组

       }

      }

      

     }

  }

  return m[1][n];

}

 

void print(int i,int j){                                                 //打印最优解方案

  if(i==j) cout<<"A"<<i;

  else{

    int k=s[i][j];               

    cout<<"(";

    print(i,k);                                                          //递归打印子问题中的矩阵和括号

    print(k+1,j);

    cout<<")";

  }

3.问题总结:

无论是递归备忘录算法还是填表算法,核心思想都是先假设i为最初的断点,然后进行从i开始的第三重循环,寻找i~j的最佳断点

区别在于递归算法在寻找最佳断点时,对k处断点的计算次数采用的是递归调用i~k和k+1~j矩阵的子问题最优解

而填表法则是从下到上,从左到右的顺序已经填好了需要用到的元素,则每次计算计算量只需调用对应位置的数组值即可

 

三.状态转移方程

 

posted @ 2020-10-16 20:05  cineny  阅读(196)  评论(0)    收藏  举报