代码随想录算法训练营第28天 | 第九章动态规划 part01

第九章 动态规划 Part 01


今天正式开始 动态规划 的学习!

理论基础

无论大家之前对动态规划的理解达到什么程度,建议都要先看一下我讲的 动态规划理论基础。这是整个动态规划学习的基石。

如果你之前没有做过动态规划的题目,读到这里可能会感觉内容简单,但其实在每道动态规划题目中都可以应用到这些基础理论,所以理解基础非常重要!

动态规划中每一个状态一定是由上一个状态推导出来的,
如下五步曲,这五步都搞清楚了,才能说把动态规划真的掌握了!

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组
    debug的最好方式就是把dp数组打印出来. 知道这些,直接开干。个人感觉B站视频没必要看,看下文章即可。

509. 斐波那契数

这道题是动态规划的入门题目,虽然简单,但它是理解 动态规划五部曲 的关键。通过这道题可以很好地掌握动态规划的基础方法。

class Solution {
public:
    int fib(int n) {
        if(n<=1)
        return n;
        else 
        return fib(n-1)+fib(n-2);
    }
};

遇到的最简单的题目,一个递归就解决了,连bug都没有改。
不过也是学习下,简单题目是用来加深对解题方法论的理解的。

  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
确实,思路非常清晰。用五步法,按照顺序学,先给点甜头尝尝。
如果代码写出来,发现结果不对,就把dp数组打印出来看看和我们推导的数列是不是一致的。


70. 爬楼梯

这道题大家可以先自己思考一下,之后你会发现它和 斐波那契数 问题有一定的联系。解决这类问题的方法可以帮助你理解如何使用动态规划来优化解决问题的效率。
这题看到和 斐波那契数 问题有一定的联系,直接就联想到,爬n层楼梯只要爬n-1层楼梯再爬一层,或者只要爬n-2层楼梯再爬两层,好家伙,不就是找规律,难得还是用公式把规律展现出来。我最开始写的是斐波那契数,但是超时了。还是得靠遍历, 遍历以后,空间复杂度,和时间复杂度大幅度下降。

class Solution {
public:
    int climbStairs(int n) {
        if(n<=2)
        return n;
        int sum1=1;
        int sum2=2;
        int temp;
        for(int i=3;i<=n;i++)
        {
            temp=sum1;
            sum1=sum2;
            sum2+=temp;
        }
        return sum2;
    }
};

746. 使用最小花费爬楼梯

这道题目在 力扣 上改过题目描述,新的描述更加清晰,明确指出第一步不需要花费。这让题目解法更加明确了。果然这题的第一步就是要去读题:给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。所以初始化 dp[0] = 0,dp[1] = 0;这一步还挺坑人的,注意一下,明白为什么即可

改后的题目相当于我文章中的 拓展 解法。
可以有两个途径得到dp[i],一个是dp[i-1] 一个是dp[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 - 1]跳还是从dp[i - 2]跳呢?

一定是选最小的,所以dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        vector<int> dp(cost.size() + 1);
        dp[0] = 0;
        dp[1] = 0;
        for (int i = 2; i <= cost.size() ; i++) {
            dp[i] = min(dp[i - 1]+ cost[i-1],dp[i - 2]+cost[i-2]);
        }
        return dp[cost.size() ];
    }
};

整体上题目还是比较简单的,核心就是找规律,只要找到规律就不难。


  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值