痛定思痛学DP,动态规划的一般性应用流程的总结

Dynamic Programming

动态规划

DP问题

【动态规划专题班】ACM总冠军、清华+斯坦福大神带你入门动态规划算法

目的:空间换时间,提升效率

方式:找出有规律性的递推公式以及限定条件

动态规划关键词

  • 每一步最优
  • 将问题划分为重复的子问题【到n-1步为整体 + 接下来的后一步】
    • 所以每次问题的形式是一致的,只是具体数据不同
    • f n = f n − 1 + 第 n 步 f_n = f_{n-1} + 第n步 fn=fn1+n
    • f i , j . . . f_{i,j} ... fi,j...
  • 把问题以归纳方式从最基础范围开始递进
    • 因为第n步的结果就是前n-1步得到的最优结果加上第n步的结果,取最优得到
    • 因此避免重复计算,需要记录每步的结果

何时使用动态规划

问结果但不是具体内容的,求的是具体一个数,一般用DP比如

  • 计数问题
    • 多少种路径
    • 多少种方法
  • 求最大最小值
    • 子序列长度(最长、最短)
    • 数字和
  • 求存在性
    • 先手是否取胜
    • 能不能选k个数使和最大

问所有种类的结果的内容,一般递归,深度搜索这些。

而,递归做了很多重复计算,因此可以将计算结果保存下来,避免重复计算。

动态问题的一般性处理步骤

一、确定状态

也就是 f n , f i , j f_n,f_{i,j} fn,fi,j之类的

问题分割为2部分:

  • 最后一步

  • 子问题

    • 把问题的规模缩小,但是问题都是一致的,同样的问题,求得范围更小

二、转移方程

就是递进的规律方程,把子问题和最后一步的联系规律找到了

比如:

f X = m i n { f X − 2 + 1 , f X − 5 + 1 , f X − 7 + 1 } f_X = min\{f_{X-2}+1, f_{X-5}+1, f_{X-7}+1\} fX=min{fX2+1,fX5+1,fX7+1}

这里表达了 f x f_x fx(最后一步)和 f X − f_{X-} fX(子问题)的关系

三、初始条件和边界情况

初始条件

最开始(小范围)的数值,用于初始化,人工设定

(这些初始值用转移方程是算不出来的,然后后面递进计算又要用到的值)

比如:

  • 一维情况下, f 0 , f 1 f_0,f_1 f0,f1
  • 二维情况下, f 0 , 0 , f 0 , 1 , f 1 , 0 f_{0,0},f_{0,1},f_{1,0} f0,0,f0,1,f1,0

这个维数和遍历对应,也和需要的变量对应

边界条件

对于每次迭代计算,都要在合理的边界下计算

比如不存在的情况下,返回null

四、计算顺序

从前往后、从小往大、从大到小等。。。

顺序原则:看计算要依赖的方向,也就是转移方程中,谁靠谁得到后续的结果

demo1 青蛙跳石头

问题:在学习视频,1:10:52的地方

  • 有n块石头分别在x轴的0,1,… n-1位置·一只青蛙在石头0,想跳到石头n-1
  • 如果青蛙在第i块石头上,它最多可以向右跳距离 a i a_i ai;
  • 问青蛙能否跳到石头n-1
  • 例子∶
    • 输入:a=[2, 3, 1, 1, 4]
    • 输出:True
    • 输入:a=[3, 2, 1, 0, 4]
    • 输出:False

思路(按照方法先自己想的)

  • 一、确定状态,分离子问题和最后一步

    蛙能否跳到n-1,能否跳到n-2,…,能否跳到1。

  • 二、确定转移方程

    f n = ( f 0 + r e s t 0 ) ∣ ∣ ( f 1 + r e s t 1 ) ∣ ∣ . . . ∣ ∣ ( f n − 1 + r e s t n − 1 ) ∣ ∣ ( f n + r e s t n ) f_n = (f_0 + rest_0) || (f_1 + rest_1) || ... || (f_{n-1} + rest_{n-1}) || (f_n + rest_n) fn=(f0+rest0)∣∣(f1+rest1)∣∣...∣∣(fn1+restn1)∣∣(fn+restn)

    r e s t i rest_i resti表示在石头 n i n_i ni处剩下需要跳的数,跳过了,就 t r u e true true

    有一个 t r u e true true就返回 t r u e true true

  • 三、确定边界和初始值

    就n块石头

    f 0 = t r u e f_0=true f0=true

  • 四、整体总结

    需要一个一维变量boolean [n] re,记录是否可以跳到对应下标的石头上

    所以,数组re应该初始化为false,能跳再改为true

  • 五、代码(没验证,凭感觉走的)

public boolean jump(int[] stone){
    // stone数组就是题目示例的数组a
    //定义数组re存结果
    n = stone.length;
    int[] re = new int[n];
    re[0] = true; //初始化
    
    for(int i = 0; i < n; i++){
        //得到在每块石头上的结果
        for(int j = 0; j < stone[i]; j++){
            if(re[j]){//代表可以跳到这块石头上
                re[i+j] = true; //把能跳的地方
            }
        }
    }
    return re[n-1];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值