动态规划-01-思维过程

本文深入探讨动态规划的核心思想,包括如何求解最值问题、利用备忘录或DP表优化穷举、确定最优子结构及状态转移方程。通过递归、记忆化和自底向上迭代三种方法实现动态规划,讲解了解决此类问题的关键步骤。动态规划适用于解决具有重叠子问题和最优子结构的最优化问题。
摘要由CSDN通过智能技术生成

动态规划思路整理:

  1. 首先,动态规划问题的一般形式就是【求最值】。比如求最长递增子序列呀,最小编辑距离等。
  2. 既然是要求最值,核心问题是什么呢?求解动态规划的核心问题是【穷举】。因为要求最值,肯定要把所有可行的答案穷举出来,然后在其中找最值。
  3. 动态规划的穷举有点特别,因为这类问题存在【重叠子问题】,如果暴力穷举的话效率会极其低下,所以需要【备忘录】或者【DP table】来优化穷举过程,避免不必要的计算。
  4. 确定【最优子结构】,列出【状态转移方程】
    在这里插入图片描述

考虑能否将问题规模减小,思考如何由较小规模问题的解获得最终问题的解

总结: 解决动态规划问题最难的地方有两点:

  • 如何定义 f(n)
  • 如何通过 f(1), f(2), … f(n - 1)推导出 f(n),即状态转移方程

1. 递归

有了状态转移方程,实际上已经可以直接用递归进行实现了。

int f(vector<int>& nums, int i)
{
    int a = 1;
    for(int j = 0; j < i; ++j)
    {
        if(nums[j] < nums[i])
        ¦   a = max(a, f(nums, j) + 1);
    }
    return a;
}

2. 自顶向下(记忆化)

递归的解法需要非常多的重复计算,如果有一种办法能避免这些重复计算,可以节省大量计算时间。记忆化就是基于这个思路的算法。在递归地求解子问题 f(1)f(1), f(2)f(2)… 过程中,将结果保存到一个表里,在后续求解子问题中如果遇到求过结果的子问题,直接查表去得到答案而不计算。

int f(vector<int>& nums, int i, vector<int>& dp)
{
    if(dp[i] != -1) return dp[i];
    int a = 1;
    for(int j = 0; j < i; ++j)
    {
        if(nums[j] < nums[i])
        ¦   a = max(a, f(nums, j) + 1);
    }
    dp[i] = a;
    return dp[i];
}

对于这种将问题规模不断减少的做法,我们把它称为自顶向下的方法。

3. 自底向上(迭代)

在自顶向下的算法中,由于递归的存在,程序运行时有额外的栈的消耗。

有了状态转移方程,我们就知道如何从最小的问题规模入手,然后不断地增加问题规模,直到所要求的问题规模为止。在这个过程中,我们同样地可以记忆每个问题规模的解来避免重复的计算。这种方法就是自底向上的方法,由于避免了递归,这是一种更好的办法。

但是迭代法需要有一个明确的迭代方向,例如线性,区间,树形,状态压缩等比较主流的动态规划问题中,迭代方向都有相应的模式。参考后面的例题。但是有一些问题迭代法方向是不确定的,这时可以退而求其次用记忆化来做。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值