● 122.买卖股票的最佳时机II ● 55. 跳跃游戏 ● 45.跳跃游戏II

● 122.买卖股票的最佳时机II

自己想的:从第一个元素开始,在自己后面连续的序列里面选中最接近自己的极大值,然后在这个极大值这卖掉,然后从极大值的下一个再开始,这么直到size()-1.

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int maxPro=0;
        for(int i=0;i<prices.size();){
            int j=i;
            while(j<prices.size()-1 && prices[j+1]>=prices[j])j++;
            if(prices[j]<prices[i]){   //没有极大值,从i的下一个开始
                i++;
                continue;
            }
            maxPro=maxPro+(prices[j]-prices[i]);
            i=j+1;          //有极大值,从j的下一个开始
        }            
        return maxPro;     
    }
};

没有注意到这个条件,很重要:“你也可以先购买,然后在 同一天 出售”,同一天可以同时买卖,所以prices[i]可以加了之后再减!

我们就是为了寻找prices[j]-prices[i]的j和i,不用往后找最大值j跳过中间那些值,而是每天都买一个新的(-prices[i]),并且看看第二天要不要卖出去(+price[i+1]):

prices[j]-prices[i]=(prices[j]-prices[j-1])+(prices[j-1]-prices[j-2])+……+(prices[i+1]-prices[i])

如果今天买明天卖赚了(prices[i+1]-price[i]>0),那就第二天卖掉(maxPro+=prices[i+1]-price[i]);如果会陪,今天不买明天不卖(maxPro+=0)。maxPro是前i-1轮的最大利润。

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

这里1买5卖,5卖10买==1买10卖。最终利润是可以分解的,这是解法核心思想

● 55. 跳跃游戏

思想:判断这个序列可跳的覆盖范围要得是:从0连续的到达最后那个元素

所以知道思想了后就是要统计连续覆盖范围右区间能到哪,在循环里判断了一下现在这个i和right是不是连接着的,然后如果到最后那个元素了就返回true。

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int right=nums[0];
        for(int i=0;i<nums.size();++i){
            if(i<=right)right=max(i+nums[i],right);//和之前统计的范围是连接的,更新
            if(right>=nums.size()-1)return true;
        }
        return false;
    }
};

整理一下得到下面更简短的代码,把i≤right作为条件,如果i大于right了肯定是不连续,就会返回false。

class Solution {
public:
    bool canJump(vector<int>& nums) {
        if(nums.size()==1)return true;
        int right=nums[0];
        for(int i=0;i<=right;++i){
            right=max(i+nums[i],right);//和之前统计的范围是由重合的,更新
            if(right>=nums.size()-1)return true;
        }
        return false;
    }
};

下面是另一种办法,从右到左明确这个覆盖范围。从nums.size()-1开始,不断向前:nums.size()-1,nums.size()-2,……找覆盖范围,如果从前面的某个地方i可以跳过来,代表这段路ok,然后再更新last,下一轮再去找前面有哪个地方能跳到last……直到last等于0,因为last=能跳过来的一个i,所以都是连续的。这样我们只需讨论能不能跳到last。

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

● 45.跳跃游戏II

感觉这三道题里最difficult的,得再琢磨琢磨。

要从覆盖范围出发,不管怎么跳,覆盖范围内一定是可以跳到的,以最小的步数增加覆盖范围,覆盖范围一旦覆盖了终点,得到的就是最少步数!

这里需要统计两个覆盖范围,当前这一步的最大覆盖(cur)和下一步最大覆盖(right)。

如果移动下标达到了当前这一步的最大覆盖最远距离了,还没有到终点的话,那么就必须再走一步来增加覆盖范围,直到覆盖范围覆盖了终点。

对每个元素,都统计第一个元素到这个元素的可覆盖下标(right),然后在这个区间里[这个元素的下一个元素,这个元素覆盖最大下标(cur)],都更新一下right。直到到了最大下标cur的时候,说明当前这个i和之前元素的right到不了最后,所以还要跳跃,count++,下一次的覆盖最大下标,应该更新为right,然后在这个区间内有right达到最大值之后就不会改变了,这个时候会跳出。也就是在下标是[0,2]的时候,right在下标1的时候更新为最大值了,是在第一步可覆盖范围(下标2)的时候才break,然后返回的。

class Solution {
public:
    int jump(vector<int>& nums) {
        if(nums.size()==1)return 0;
        int right=0;
        int cur=0;
        int count=0;
        for(int i=0;i<nums.size();++i){
            right=max(right,nums[i]+i);     
                 
            if(i==cur)//到达当前的最大范围
            {
                cur=right;
                count++;
                if(right>=nums.size()-1)break; //在[3,1]之间,到3的时候已经把right更新到n-1
            }
            
        }
        return count;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值