赞
踩
动态规划,英文:Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。所以动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的。
例如:有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。动态规划中dp[j]是由dp[j-weight[i]]推导出来的,然后取max(dp[j], dp[j - weight[i]] + value[i])。但如果是贪心呢,每次拿物品选一个最大的或者最小的就完事了,和上一个状态没有关系。所以贪心解决不了动态规划的问题。
动态规划五部曲:
确定dp数组(dp table)以及下标的含义
确定递推公式
dp数组如何初始化
确定遍历顺序
举例推导dp数组
在代码中找问题的最好方式就是把dp数组打印出来,看看究竟是不是按照自己思路推导的!
递归解法:
- class Solution:
- def fib(self, n: int) -> int:
- if n == 0 or n == 1:
- return n
- else:
- return self.fib(n - 1) + self.fib(n - 2)
动态规划:
1.确定dp数组及其下标的含义:dp[i]的含义为第i个数的斐波那契数值是dp[i];
2.确定递推公式:状态转移方程 dp[i] = dp[i - 1] + dp[i - 2];
3.dp数组如何初始化:dp[0] = 0,dp[1] = 1;
4.确定遍历顺序:从递归公式dp[i] = dp[i - 1] + dp[i - 2]中可以看出,dp[i]是依赖 dp[i - 1] 和 dp[i - 2],那么遍历的顺序一定是从前到后遍历的;
5.举例推导dp数组:按照这个递推公式dp[i] = dp[i - 1] + dp[i - 2],当n为10的时候,dp数组应该是:0 1 1 2 3 5 8 13 21 34 55。
- class Solution:
- def fib(self, n: int) -> int:
- if n <= 1:
- return n
- dp = [0] * (n + 1) # 创建dp数组
- dp[0], dp[1] = 0, 1 # dp数组初始化
- for i in range(2, n + 1): # 从前向后遍历
- dp[i] = dp[i - 1] + dp[i - 2]
- return dp[n]
动规五部曲:
1.确定dp数组以及下标的含义:dp[i]爬到第i层楼梯,有dp[i]种方法;
2.确定递推公式:从dp[i]的定义可以看出,dp[i] 可以有两个方向推出来。首先是dp[i - 1],上i-1层楼梯,有dp[i - 1]种方法,那么再一步跳一个台阶就是dp[i]。还有就是dp[i - 2],上i-2层楼梯,有dp[i - 2]种方法,那么再一步跳两个台阶就是dp[i]。那么dp[i] = dp[i - 1] + dp[i - 2] 。
3.dp数组如何初始化:dp[i]的定义:爬到第i层楼梯,有dp[i]种方法。所以dp[1] = 1,dp[2] = 2,然后从i = 3开始递推;
4.确定遍历顺序:从递推公式dp[i] = dp[i - 1] + dp[i - 2]可以看出,遍历顺序一定是从前向后遍历的
5.举例推导dp数组:举例当n为5的时候,dp table(dp数组)应该是1 2 3 5 8.
- class Solution:
- def climbStairs(self, n: int) -> int: # 与斐波那契数基本一致
- if n <= 2:
- return n
- dp = [0] * (n + 1)
- dp[0], dp[1], dp[2] = 0, 1, 2
- for i in range(3, n + 1):
- dp[i] = dp[i - 1] + dp[i - 2]
- return dp[n]
动归五部曲:
1.确定dp数组及其下标含义:dp[i]表示的是爬到第i级台阶所需费用的最小值;
2.确定递推公式:dp[i - 1] 跳到 dp[i] 需要花费 dp[i - 1] + cost[i - 1]。dp[i - 2] 跳到 dp[i] 需要花费 dp[i - 2] + cost[i - 2]。因为要取最小值,所以公式为 dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
3.dp数组如何初始化:dp[0] = 0,dp[1] = 0;
4.确定遍历顺序:因为dp[i]由dp[i-1]dp[i-2]推出,所以是从前到后遍历cost数组;
5.举例推导dp数组:例2对应的dp数组为[0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 6]。
- class Solution:
- def minCostClimbingStairs(self, cost: List[int]) -> int:
- dp = [0] * (len(cost) + 1) # 创建dp数组并初始化
- for i in range(2, len(cost) + 1): # 从前向后遍历
- dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]) # 递推公式
- return dp[-1] # 最后一项为爬到楼顶的消耗
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。