打卡(25)贪心集锦

本文探讨了如何使用贪心策略解决LeetCode中的几个问题,包括跳游戏系列、股票买卖、加油站挑战和字符删除重复等问题。通过实例展示了如何利用贪心思想简化求解过程,并介绍了动态规划和最长递增序列在这些问题中的对比。
摘要由CSDN通过智能技术生成

https://leetcode-cn.com/problems/jump-game/
简单贪心
计算当前能到达的最大距离,计算所有满足小于等于最大距离点,是否可以更新最大距离,以此类推到最后一个元素

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

自己的思路,最近有点被动态规划洗脑了。于是我就倒着动态规划了,时间复杂度是一如既往的大。
倒推,看某个点可以到达的所有点之中是否有可以到达最后一个点的点。从后向前推导到第一个元素。

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int n=nums.size();
        int could[100010]={};
        could[n-1]=1;
        for(int i=n-2;i>=0;i--){
            for(int j=1;j<=nums[i];j++){
                int tag=j+i;
                if(could[tag]){
                    could[i]=1;
                    break;
                }
            }
        }
        return could[0]==1;
    }
};

LeetCode名字叫‘大力哥’,喝大力有好身体!giao!

https://leetcode-cn.com/problems/jump-game-ii/

class Solution {
public:
    int jump(vector<int>& nums){
        int n=nums.size();
        int step[100010]={};
        for(int i=n-2;i>=0;i--){
            step[i]=10000;
            for(int j=nums[i];j>=1;j--){
                if(i+j>=n-1){ 
                    step[i]=1;
                    break;
                }
                if(step[i+j]!=-1){
                    step[i]=min(step[i],1+step[i+j]);
                }
            }
            if(step[i]==10000) step[i]=-1;
        }
        return step[0];
    }
};

终究还是动态规划了,等我看下贪心怎么做。

跳跃游戏II
https://leetcode-cn.com/problems/jump-game-ii/
贪心:
一开始没看懂是为什么这样可以的,后来搞明白了,让我来给出数学证明吧:
[a,……,b,……,c,……,d]假设最佳的路线为a->b->c->d,设为最佳路线A:
那么就满足如下的约束条件:
(1)首先,每个A中元素的后继都必然可以到达:
例如:nums[a]+a>=b
(2)其次,每个A中的元素的后继的后继,(假如有的话),必然不可到达。
例如:nums[a]+a<   \, c,注意是严格小于。

那么我们设置边界,边界例如为limit,表示某个区间中的元素能到达的最远位置为limit,则区间[a,b]的边界必然是在哪里呢,必然是出于区间[b,c)中,因为必须要包含b,而必然不能包含c,直接按照最大的区间来放缩,那就假设是这个区间,(足够讨论问题)。边界随着区间的顺次枚举不断更新,这个时候我们发现所有出现的边界的特点都满足:
当前区间的边界必然位于下一个的相邻区间中,这样的话,其实边界的个数,等于路线的步骤数目减去1。为了修正这个1。可以手动加上去,也可以设置0为第一个边界,那么每次遇到边界,数目就++,最终得到的结果就是跳跃的步数。

class Solution {
public:
    int jump(vector<int>& nums){
        int deep=0,n=nums.size();
        int mmax=0,ans=0;
        for(int i=0;i<n-1;i++){
            mmax=max(mmax,nums[i]+i);
            if(deep==i){
                ans++;
                deep=mmax;
            }
        }
        return ans;
    }
};

买卖股票,贪心+最长递增序列
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/

找到一段最长的递增子序列,用最大值减去最小值,作为这一段的收益,显然可以证明,这样是收益最大的,否则会有中途一个区间的小损失。
例如[1 2 3 4 5 6],1和6收益最大是5,如果分成好几段,就有[1 2][3 4][5 6]可以看到,收益部分就缺少了[2 3][4 5]这些部分,可以得到
结论,只有最大最小相减才是最优解法。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int st=0,end=0;
        int n=prices.size();
        int ans=0;
        for(int i=0;i<n;i++){
            st=prices[i];
            while(i+1<n){
                if(prices[i]<prices[i+1]) i++;
                else break;
            }
            end=prices[i];
            ans+=end-st;
        }
        return ans;
    }
};

https://leetcode-cn.com/problems/gas-station/
最近做的顺风顺水了,不过就是复杂度还是高,虽然能过,毕竟都是一些简单题目。
贪心的精髓在于,找到可以忽略的一些中间过程,直接从下一个可能的解的位置可以计算,节省算力。
就像这个加油站题目,只要模拟一下,然后下一次要从不能到达的第一个点开始接着找,因为已经假定第一个开始的点是净收益大于0的,那么去掉第一个点之后,其他的点更加不可能到达,类似放缩的思想在这里。

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int sum=0;
        int net[10010];//净收益
        int n=gas.size();
        for(int i=0;i<n;i++)
            net[i]=gas[i]-cost[i];
            
        for(int i=0;i<n;i++){
            if(net[i]>=0){
                int j=i;
                int sum=net[i];
                int k=n;
                while(--k){
                    int tag=(++j)%n;
                    sum+=net[tag];
                    if(sum<0) break;
                }
                if(k==0) return i;
                else i=j;
            }
        }
        return -1;
    }
};

单调栈+贪心
https://leetcode-cn.com/problems/remove-duplicate-letters/
从前往后找的时候,如果这个字母之后还会再出现,就可以按照字典序大胆弹出去,否则就不弹出去。

class Solution {
public:
    string removeDuplicateLetters(string s) {
        int vis[26]={},num[26]={};//数组要初始化,否则可能乱码,这个错误还犯。。。
        for (char ch : s)
            num[ch - 'a']++;
        string stk;
        for (char ch : s) {
            if (!vis[ch - 'a']) {
                while (!stk.empty() && stk.back() > ch) {
                    if (num[stk.back() - 'a'] > 0) {
                        vis[stk.back() - 'a'] = 0;
                        stk.pop_back();
                    } else {
                        break;
                    }
                }
                vis[ch - 'a'] = 1;
                stk.push_back(ch);
            }
            num[ch - 'a'] -= 1;
        }
        return stk;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值