动态规划学习笔记

前言

动态规划问题一直是算法学习中的一个难点问题,但也是一个重点问题。在这次的面试中也遇到了动态规划的相关问题。
本文是对大神labuladong的一篇关于动态规划文章的个人摘要,原文请移步动态规划详解(修订版)

动态规划三要素

动态规划问题的本质其实就是通过穷举得到一个问题的最优解,只是能够通过动态规划解决的问题都会满足下面三个要素。

重叠子问题

如果单纯使用暴力穷举的话,效率会非常低下,所以动态规划问题常常会用备忘录或者dp table保存计算的中间状态结果。

最优子结构

动态规划问题必须满足最优子结构,这样才能通过子问题的最优解得到目标问题的最优解。

状态转移方程

如何从一个子问题的最优解计算出下一个子问题的最优解,从而最终计算出目标问题的最优解,这中间的计算公式就是我们要找出的状态转移方程。
找出状态转移方程是动态规划问题中最难的一步,下面提供一个找出动态转移方程的思维框架:
明确状态–>明确dp数组/函数的意义–>明确选择–>明确base case

凑零钱问题说明如何找到状态转移方程

给你k种面值的硬币,面值分别为c1, c2 …
ck,每种硬币的数量无限,再给一个总金额amount,问你最少需要几枚硬币凑出这个金额,如果不可能凑出,算法返回 -1 。

先确定状态,也就是原问题和子问题中变化的变量,由于硬币数量无限,所以唯一变化的量就是金额amount。
再确定dp函数的定义,函数dp(n)表示,当前的目标金额是n,至少需要dp(n)个硬币凑出金额。
然后确定选择并择优,*也就是对于每个状态,可以做出什么选择改变当前状态。*具体到这个问题,无论当前的目标金额是多少,选择就是从面额列表中选择一个硬币,然后目标金额就会减少。

伪代码框架
def coinChange(coins:List[int],amount:int)
//定义:要凑出金额n,至少需要dp(n)个硬币
	def dp(n):
		//做选择,需要硬币最少的那个结果就是答案
		for coin in coins:
			res = min(res, 1 + dp(n-coin))
		return res
	//目标金额是amount
	return dp(amount);

最后明确base case,显然目标金额是0时,需要的硬币个数为0;当目标金额小于0时,无解,返回-1:

def coinChange(coins:List[int],amount:int)
//定义:要凑出金额n,至少需要dp(n)个硬币
	def dp(n):
		// base case
		if n == 0: return 0
		if n < 0: return -1
		//求最小解,所以初始化为正无穷
		res = float('INF')
		//做选择,需要硬币最少的那个结果就是答案
		for coin in coins:
			subproblem = dp(n - coin)
			//子问题无解,跳过
			if subproblem == -1:continue
			res = min(res, 1 + subproblem )
		return res if res != float('INF') else -1
	//目标金额是amount
	return dp(amount);

用数学形式表示状态转移方程(是不是很像数学归纳法的写法):
在这里插入图片描述
至此,这个问题已经解决了,只是要解决下重叠子问题。解决重叠子问题的方法,

  • 一是通过备忘录的方式,对于子问题的结果优先从备忘录中获取
  • 二是用dp table,自底向上的计算子问题
int coinChange(vector<int> &coins, int amount)
{
    //初始化dp table
    //数组大小为amount+1,初始值也为amount+1
    vector<int> dp(amount+1,amount+1);
    //base case
    dp[0] = 0;
    for (int i = 0; i < dp.size(); i++)
    {
        for (int coin : coins)
        {
            //子问题无解,跳过
            if (i - coin < 0) continue;
            dp[i] = min(dp[i - coin] + 1,dp[i]);
        }
    }
    return (dp[amount] == amount + 1) ? -1 : dp[amount];
}
	

在这里插入图片描述

动态规划经典问题解析

0-1背包问题

有一个容量为M的背包,有N个物品,物品的重量为wgt[],物品的价值为val[],如何装物品可以获得最大价值

首先确定这是一个动态规划的问题,满足动态规划的三个因素。然后按照需要列出动态转移方程,套用上面的四大步骤:

  1. 明确状态
    本题的状态是背包的容量和可选的物品还剩多少
  2. 确定动态函数/dp数组的含义
    dp数组的维度通常是根据状态的维度来确定的,比如dp[i][w]的含义是对于前i个物品,当背包容量为w时,可以装下的最大价值是dp[i][w]。其实这就是一个中间状态,状态转移的起始点。同时也可以得出base case是dp[0][w] = 0,dp[i][0] = 0。
  3. 确定选择和择优
    背包问题的选择就是放入i个物品或者不放入i个物品。择优是在选择之后,背包容量还有剩余,而且价值大于当前价值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值