动态规划之深入浅出

1. 简介

动态规划(Dynamic Programming,DP)算法目的为解决多阶段决策最优化问题,采取的方法是将待求解的问题分解为多个子问题,按顺序求解每一个子问题,当前子问题的解将由前一个子问题的解推导出,最后一个子问题就是初始问题的解。

由于动态规划解决的问题多数有重叠子问题这个特点,为减少重复计算,对每一个子问题只解一次,将其不同阶段的不同状态保存在一个二维数组中,以便下一次求解同一子问题时直接查表。

动态规划求解最优化问题仅需多项式时间复杂度,比回溯法、暴力法等效率更高,当问题具有多个可行解,且要求寻找多个可行解中的最大值或者最小值时,往往可采取动态规划求解。

2. 设计步骤

动态规划的设计步骤如下:

  1. 寻找最优子结构:按照问题的时间或空间特征,把问题分为若干个阶段。划分后的阶段一定要有序或者可排序,否则无法求解;
  2. 设计递归公式,递归地定义最优值的值;
  3. 以自底向上或自顶向下的记忆化方式(备忘录法)计算出最优值;
  4. 根据计算得到的信息,构造一个最优解。

3. 实例讲解

题目:给定一个三角形队列,从最顶端往下走,寻找和最小的路径。

分析:

由于从顶端往下走有多条路径,问题具有多个可行解,并且每一条路径的和不一样,而题目要求和最小,显然该问题属于动态规划问题。

下面,按照动态规划的设计步骤解决该问题:

1. 寻找最优子结构

    除了最底层外,每一层的每个节点均有两个选择,从“2”出发,可选择“3”或者“4”,因此从“2”出发的最优路径,为从“3”出发或者从“4”出发的最优路径中最小的一条。而从“3”出发的最优路径,为从“6”出发或者从“5”出发的最优路径中最小的一条。

2. 设计递归公式

   根据上述分析,用f(i,j)表示从点(i,j)出发的最优路径的和,可得递归公式:

                   f(i,j)=min\begin{Bmatrix}f(i+1,j),f(i+1,j+1) \end{Bmatrix} +val(i,j)

其中,val(i,j)表示点(i,j)的值。

3. 计算最优值

  我们分别以自底向上或自顶向下两种方法求解该问题:

自底向上:由于上一层的最优解取决于下一层,因此从最底层开始计算比较容易理解,最底层的最优值就是节点本身的值,可从倒数第二层开始求解。

int minimumTotal(vector<vector<int> > &triangle) {
    int row=triangle.size();
    vector<vector<int> > f(triangle);

    for(int x=row-2;x>=0;x--)
        for(int y=0;y<=x;y++)
            f[x][y]=min(f[x+1][y],f[x+1][y+1])+triangle[x][y];
    return f[0][0];
}

自顶向下:该方法是递归形式的,且携带备忘录,不会计算重复子问题。

int minimumTotal(vector<vector<int> > &triangle) {
    int row=triangle.size();
    vector<vector<int> > f(triangle);
    for(int m=0;m<row-1;m++)
        for(int n=0;n<=m;n++)
            f[m][n]=INT8_MAX;  //与自底向上的方法不同,备忘录法必须将其初始化为标识值,以便“查找备忘录”
    f[row-1]=triangle[row-1];       //最后一行保持原值
    return dp(0,0,triangle,f);      //从根出发
}

int dp(int x,int y,vector<vector<int> > &triangle,vector<vector<int> > &f){
    if (f[x][y] != INT8_MAX) 
        return f[x][y];   //查找备忘录,如果已经计算过,直接返回,避免重复计算
    f[x][y]=min(dp(x+1,y,triangle,f),dp(x+1,y+1,triangle,f))+triangle[x][y];
    return f[x][y];
}
发布了126 篇原创文章 · 获赞 219 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/yz930618/article/details/86704441