动态规划总结:题目类型-设计流程-框架

1.直接考虑动态规划的题目类型:

求最值:因为最优子结构性质作为动态规划问题的必要条件,一定是让你求最值的,以后碰到那种恶心人的最值题,思路往动态规划想就对了,这就是套路。

子序列:因为子序列类型的问题,穷举出所有可能的结果都不容易,而动态规划算法做的就是穷举 + 剪枝,它俩天生一对儿。所以可以说只要涉及子序列问题,十有八九都需要动态规划来解决,往这方面考虑就对了。

一旦涉及到子序列和最值,那几乎可以肯定,考察的是动态规划技巧,时间复杂度一般都是 O(n^2)

2.总结一下动态规划的设计流程:

首先明确 dp 数组所存数据的含义。这步很重要,如果不得当或者不够清晰,会阻碍之后的步骤。

然后根据 dp 数组的定义,运用数学归纳法的思想,假设 dp[0…i−1] 都已知,想办法求出 dp[i],一旦这一步完成,整个题目基本就解决了。

但如果无法完成这一步,很可能就是 dp 数组的定义不够恰当,需要重新定义 dp 数组的含义;或者可能是 dp 数组存储的信息还不够,不足以推出下一步的答案,需要把 dp 数组扩大成二维数组甚至三维数组。

3.动态规划的模板框架

带备忘录的递归-自顶向下

自顶向下主要是定义dp函数
参数:状态
返回值:要求的数值
备忘录:memo[state],=初始化值,还未计算过;!=初始化值,状态state的结果值

先初始化整个备忘录,用一个正常情况下结果取不到的值进行初始化整个备忘录,说明还没被计算过。例如结果都是大于零的数,则初始化为0;如果结果能取到零,就初始化为-1: memo(memo,-1,sizeof(memo))
注意 如果用特殊值表示“还没计算过“,必须和其他特殊值区(比如无解)区分开。

//这里是把结果数组和备忘录合在一起,如果答案和标记不能用正负/0区分,就单独用dp[]记录答案,用memo记录是否计算过

int dp(int state)
{
    1.在备忘录中检查,若有直接return备忘录:if(memo[state]>=0) return memo[state];
    
    2.base case:给定最初始的状态返回值
    
    3.初始化memo[state]:如果是求最大值,就初始化为0,如果求最小值,就初始化为INF或者所有状态可能取到的最大值//为了求最值
    
    4.开始自顶向下:
    for 选择:(选择就是能使状态发生变化的动作)
    	{
	        该选择是否使得状态转移到一个有效的状态,否:continue
	        memo[state]=min/max{memo[state],状态转移方程}
	    }
    
    5.记入备忘录
    
    return memo[state]
迭代法-自底向上

迭代法不需要备忘录,因为他是自底向上的,不会出现重复计算的情况,所以也不需要判断是否计算过

自底向上主要是定义数组dp[i]
i是状态
dp[i]是状态i对应的答案

给出base case的结果

初始化dp[i]:求最小值,就初始化为INF(或者该状态可能取到的最大值);求最大值,就初始化为0
//这一步和上面初始化memo[state]一样,是为了求最值,不是初始化备忘录

自底向上
    for 状态(从base case开始):
        for 选择(每个能使状态发生改变的动作):
        {
            是否使状态到达有效状态,否则continue
            dp[i]=min/max(dp[i],状态转移方程)
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值