算法训练营DAY32|122.买卖股票的最佳时机II、55. 跳跃游戏、45.跳跃游戏II

三道题都很巧妙,没有接触过贪心算法,或者第一次接触该题型的都很难做得出。

先看第一道,第一道题是股票系列的题型,实际上它们主要是应用于动态规划,但是这里这道题也是可以用贪心来做,并且代码十分的简单。


122. 买卖股票的最佳时机 II - 力扣(LeetCode)icon-default.png?t=MBR7https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/乍一看题目要求十分困难,求出总利润最高,要求是某一天买某一天卖,只能同时持有一支股票,但是可以在卖出后再其他时间再次购买彩票并卖出,这一点是十分重要的,这也间接的加大了难度。

解题思路是:我们可以将任意天数购买和卖出的等式简化为以下形式,例如我们购买该彩票的时间为第0天而在第3天时卖出,那么对于prices为数组名,有prices[3]-prices[0]=(prices[3]-prices[2])+(prices[2]-prices[1])+(prices[1]-prices[0])。这一等式可以将其看作为通式,它适用于任何天数的购买和卖出,都有售出价格减去购买价格等于其从卖出下标到购买下标方向上的左闭右闭区间相邻数的差再相加。这一结论可以用任意数组来模拟,都是正确的,起初看到这一方法觉得十分的神奇,后来想了一想由于是左闭右闭区间,相邻数做差再相加,那么说明我们的购买钱数和卖出钱数实际都参与了运算,此时我们将它们两个中间的数据作为一个整体,设购买钱数为x,卖出为y,那么很容易知道y-x=y-z+(z-x)。所以等式一定是成立的。我们根据这一特点,可以想到如果有一数组来表示给定数组prices中相邻两个数的差,那么将正数相加获得的答案自然就是买卖股票的最大利润。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int result=0;
        for(int i=1;i<prices.size();i++){
            result+=max(prices[i]-prices[i-1],0);
        }
        return result;
    }
};

知道了解题思路,不难写出以上代码,这里我们用result的重复判断来避免了新开数组的空间开辟,从而使代码变得简洁一些,实际上这和开辟一个数组存储相邻差,再取正数做和思路是相同的。


55. 跳跃游戏 - 力扣(LeetCode)icon-default.png?t=MBR7https://leetcode.cn/problems/jump-game/也是道没做过很难做的题目,思路是不要纠结于本次的前进究竟要走到哪一个位置,而是明确走的这一步它的最大范围能够覆盖到哪?如果覆盖范围能够包括最后一个位置,那么就返回true。而每一次的for以当前能够走到的最远距离作为判断的范围。

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int cover=0;
        for(int i=0;i<=cover;i++){
            cover=max(i+nums[i],cover);
            if(cover>=nums.size()-1)return true;
        }
        return false;
    }
};

cover每次对当前数据进行比较,如果比现有范围大就赋值给它,判断部分的i<=cover在这里起的是什么作用呢?i每次都++如果cover所覆盖的范围都是0,那么就直接跳出循环了,每次它是用来约束i的,用来判断到底有没有可行的范围可以走,那有人问,如果是{2,0,1,0}怎么办呢?这就凸显出nums【i】+i的作用了,从当前下标开始最多能走到哪,所以加i是十分重要的,不能够缺少。

至于为什么cover>=nums.size()-1呢,是因为最后一个数下标是这个,所以我们判断覆盖范围大于或等于直接返回true了。

实际上这种方法是十分巧妙的,可以随意模拟数组将代码带入进行解析,这种cover赋值更大范围的思想,有效的帮助我们解决如果本次可以走的范围很多,那么到底走向哪里的问题,不用像递归思路那样走错了需要回溯走下一个位置,因为即使走错了,下一步的cover范围覆盖可以帮助我们修改,十分的巧妙。例如数组{2,0,1,2,1,3,0,1,0}比如说我们走到第二个2时候,如果是回溯思想可能就会先走第二个1试一试,不行再走3试一试,不过这里的cover会直接覆盖掉上个答案,也就是说他尽可能取得最大的步骤。


45. 跳跃游戏 II - 力扣(LeetCode)icon-default.png?t=MBR7https://leetcode.cn/problems/jump-game-ii/跳跃游戏II和跳跃游戏的前部分思路是一致的,都是要确立覆盖的范围,而并非要一次确定好走哪一个下标处,不同的是,这道题目不仅要求要走到最后一个位置,还要求要使得走的步数最小。

这该怎么办呢?我们需要创建两个变量来存储能覆盖的范围,一个是当前的另一个是下一步能覆盖到的范围,那么我们如何知道当前这一步的范围内,下一步最远能走多远呢?答案是我们用循环变量i来控制,如果不停的更新next的最大值,当i等于当前的最大范围时,进入判断,把next值直接赋值给当前的最大范围,然后判断此范围是否包含最后一个数的下标,不包含接着循环这些步骤,直到到达。

class Solution {
public:
    int jump(vector<int>& nums) {
        if(nums.size()==1)return 0;
        int count=0;
    int cur=0;
    int next=0;
    for(int i=0;i<nums.size();i++){
        next=max(nums[i]+i,next);
       if(i==cur){
           if(cur<nums.size()-1){
               cur=next;
               count++;
           }
           else break;
       }
       if(cur>=nums.size())break;
    }
        return count;
    }
};

我们把判断跳出的部分写在了第一个if判断的后面,这样可以避免一些时间的浪费,提高效率,不过理论上来上说,放进去也是可以的


以上代码均可ac

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学习算法的杨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值