贪心算法OJ

1. 简单贪心算法(常识问题)

到底什么是贪心算法:利用局部最优来推出全局最优就是核心

1.1 LeetCode第455题—分发饼干

在这里插入图片描述

局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩

class Solution {
public:
    //我们要的结果是尽可能的将我们的饼干分发给更多的小孩,如果我们这么思考应该怎么想呢?
    //尽可能让大饼干去满足大胃口的人
    //局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩。
    int findContentChildren(vector<int>& g, vector<int>& s) {
        sort(g.begin(),g.end());
        sort(s.begin(),s.end());
        int index = s.size()-1;
        int num = 0;
        for(int i = g.size()-1;i>=0;--i)
        {
            if(index >= 0 && s[index] >= g[i])
            {
                num++;
                index--;
            }
        }
        return num;
    }
};

1.2 LeetCode第1005题—K此取反后最大化的数组和

在这里插入图片描述

class Solution {
static  bool cmp(int a,int b)
{
    return abs(a) > abs(b);
}
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        //如果数组中所有的元素都是正数,那么我们应该尽可能的让最小的哪一个进行转换,
        //如果数组中有负数,那么我们应该尽可能的让较小的负数来做这一次的转换
        //这道题并没有让你返回下标,而是返回结果
        sort(nums.begin(),nums.end(),cmp);
        for(int i = 0;i<nums.size();++i)
        {
            if(nums[i] < 0 && k > 0)
            {
                nums[i] *= -1;
                k--;
            }
        }
        while(k--)
        {
            nums[nums.size()-1] *= -1;
        }
        int ret = 0;
        for(int& e : nums)
        {
            ret += e;
        }
        return ret;
    }
};

1.2 LeetCode第860题—柠檬水找零

在这里插入图片描述
因为5元既可以给10元的找钱,也可以给20元的找钱,但是10元的只能够给20元的找钱,所以我们尽可能的,在找20元的时候,优先把10元的找出去,这样才能够尽可能的给每一个人都把钱找了

class Solution {
public:
    bool lemonadeChange(vector<int>& bills) {
        //5元是不需要找零钱的,因为单价本身就是5元
        //如果一上来就是超5元的,那么不需要继续下去了直接返回false
        //5美元是万能的,所以我们在收到20美元的时候,应该尽可能的先把10元的用出去
        int five = 0,ten = 0;
        for(auto& bill : bills)
        {
            if(bill == 5)
            {
                five++;
            }
            else if(bill == 10)
            {
                if(five < 0)
                    return false;
                ten++;
                five--;
            }
            else
            {
                if(five > 0 && ten > 0)
                {
                    five--;
                    ten--;
                }
                else  if(five >= 3)
                {
                    five -= 3;
                }
                else
                {
                    return false;
                }
            }
        }
        return true;
    }
};

2. 中等贪心算法

2.1 LeetCode第376题—摆动序列

在这里插入图片描述
有可能是会出现[3,3,3,5]的情况的,所以这里就相乘波峰和波谷交替来增加最长的摆动序列。

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        int n = nums.size();
        if (n < 2) 
            return n;
        int curdiff = 0;
        int prevdiff = 0;
        int result = 1;
        for(int i = 0;i<n-1;++i)
        {
            curdiff = nums[i+1] - nums[i];
            if((curdiff >0 && prevdiff<= 0) || (curdiff < 0 && prevdiff>= 0))
            {
                result++;
                prevdiff = curdiff;
            }
        }
        return result;
    }
};

2.2 LeetCode第122题—买卖股票的最佳时机II

在这里插入图片描述

  • 局部最优:收集每天的正利润,全局最优:求得最大利润.
  • 在这里插入图片描述
class Solution {
public:
    //必须在购买前先将手里的股票卖掉
    //局部最优:收集每天的正利润,全局最优:求得最大利润
    //当然这道题也可以使用动态规划来做
    int maxProfit(vector<int>& prices) {
        int ret = 0;
        //第一天买入第五天卖出,其实这个是可以把他进行拆分的
        for(int i = 1;i<prices.size();++i)
        {
            ret += std::max(prices[i] - prices[i-1],0);
        }
        return ret;
    }
};

3. LeetCode第435题—无重叠区间

在这里插入图片描述
我们依旧按照右边界进行排序,来记录要删除的区间个数

class Solution {
public:
    //我们来记录要删除的区间
    //我们根据右边界来进行排序
    static bool cmp(vector<int>& a, vector<int>& b)
    {
        return a[1] < b[1];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        sort(intervals.begin(),intervals.end(),cmp);
        int del = 0;
        int t = intervals[0][1];
        for(int i = 1;i<intervals.size();++i)
        {
            if(intervals[i][0] < t)
                del++;
            else 
                t = intervals[i][1];
        }
        return del;
    }
};

4. LeetCode第406题—根据身高建队列

LeetCode题目链接:https://leetcode-cn.com/problems/queue-reconstruction-by-height/
在这里插入图片描述
其实就是把这个调整到合适的位置,其实这道题还是挺有意思的。
排序,根据身高,然后对于身高相同的,就根据第二个参数进行排序,对于第二个参数也就是每个需要插入的位置,所以我们就拿到这个位置,然后将对应的vector< int >插入其中,但是使用vector这里就会效率很慢,所以最好的方式在插入的部分,我们使用list来解决
在这里插入图片描述

class Solution {
public:
    static bool cmp(const vector<int> a, const vector<int> b) {
        if (a[0] == b[0]) 
            return a[1] < b[1];
        return a[0] > b[0];
    }
    //使用list的确要快很多
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        sort (people.begin(), people.end(), cmp);
        //list可以在任意位置进行插入删除,时间复杂度位O(1)效率更高
        vector<vector<int>> res;
        list<vector<int>> que;
        for (int i = 0; i < people.size(); i++) {
            int position = people[i][1];
            std::list<vector<int>>::iterator it = que.begin();
            while(position--)
                it++;
            que.insert(it,people[i]);
        }
        return vector<vector<int>>(que.begin(),que.end());
    }
};

在这里插入图片描述
使用vector来解决

class Solution {
public:
    static bool cmp(const vector<int> a, const vector<int> b) {
        if (a[0] == b[0]) 
            return a[1] < b[1];
        return a[0] > b[0];
    }
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        sort (people.begin(), people.end(), cmp);
        vector<vector<int>> res;
        for (int i = 0; i < people.size(); i++) {
            int position = people[i][1];
            res.insert(res.begin() + position, people[i]);
        }
        return res;
    }
};

在这里插入图片描述

5. LeetCode第763题—划分字母区间

LeetCode题目链接:https://leetcode-cn.com/problems/partition-labels/
在这里插入图片描述
解题思路:
在这里插入图片描述

class Solution {
public:
    //必须要让每个片段中的字母就出现在这个片段中,别的片段中不能在出现该字母,来进行划分
    //当你所不断跟新迭代下标,当你的下标值和你当前的下标值一样的时候,这一段的后面就不会在出现字符
    vector<int> partitionLabels(string s) {
        vector<int> hash(26,0);
        for(int i = 0;i<s.size();++i)
            hash[s[i]-'a'] = i;
        vector<int> v;
        int left = 0;
        int right = 0;
        for(int i = 0;i<s.size();++i)
        {
            right = max(right,hash[s[i]-'a']);
            //此时这里就是分割线
            if(i == right)
            {
                v.push_back(right-left+1);
                left = right+1;
            }
        }
        return v;
    }
};

6. LeetCode第452题—用最少数量的箭引爆气球

LeetCode题目链接:https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/
在这里插入图片描述
这道题就是沿着y轴射出去一支箭,对于只要穿过X的begin到end的任意一个位置都一样将该气球射穿,那么我们这里维护一个右边界,简单点说就是:在x等于6的地方沿着y轴射出一支箭,那么这一支箭,将会使气球1和气球2击穿,当下一个气球的左边界还要大于所维护的最小右边界的时候,说明此时这个气球无法做到和前面的气球同时被击穿,所以需要新增加一支箭
在这里插入图片描述

class Solution {
public:
    //我发现这类题目就好难读懂呀
    //从y的方向角度射出去一只箭,对于这个箭来说,只要进行区间那么就可以射破
    static bool cmp(vector<int>& a , vector<int>& b)
    {
        return a[0] < b[0];
    }
    int findMinArrowShots(vector<vector<int>>& points) {
        if(points.size() == 0)
            return 0;
        sort(points.begin(),points.end(),cmp);
        int num = 1;
        int minRight = points[0][1];
        for(int i = 1;i<points.size();++i)
        {
            if(points[i][0] > minRight)
            {
                num++;
                minRight = points[i][1];
            }
            else
            {
                //这里的思想应该怎么思考,才是这道题最复杂的地方
                minRight = std::min(minRight,points[i][1]);
            }
        }
        return num;
    }
};

7. LeetCode第134题—加油站

LeetCode题目链接:https://leetcode-cn.com/problems/gas-station/
在这里插入图片描述
解题思路:
从局部最优,去推导全局最有的情况

  1. 你要确保从i位置出发,能够开到i+1的位置(如果从[0,i],发现在第i处的时候,为负了,那么起始位置就应该设置为i+1,为什么呢?起始可以抽象为A,B两个点的,把[0,i]这一段都抽象为A,那么A->B是过不去了,那么下一个起始位置就应该设置为下一个点)
  2. 所有站里的油总量要>=车子的总耗油量
class Solution {
public:
    //对于这道题其实就是局部最优得到全局最优
    //是否是跑完整个过程,我们可以这样来思考
    //首先你得从i 跑到 i+1的位置,最起码这个过程是要保证油量的足够 >= 0
    //第二点就是你要保证,到达i位置的时候,总的剩余油量>= 0
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int start = 0;
        int curSum = 0,tatalSum = 0;
        for(int i = 0;i<gas.size();++i)
        {
            //计算[0,i]这一段
            curSum += gas[i] - cost[i];
            tatalSum += gas[i] - cost[i];
            if(curSum < 0)
            {
                //此时说明这一段从0 -> i这段路是走不通的
                //那么从i+1这段开始,重新计算
                start = i+1;
                curSum = 0;
            }
        }
        return tatalSum >= 0 ? start:-1;
    }
};

8. LeetCode第135题—分发糖果

LeetCode题目链接:https://leetcode-cn.com/problems/candy/
在这里插入图片描述

  1. 从左边往右边遍历,来对糖果的数量进行求解
  2. 从右边往左边遍历,这里是会出现一点小问题的,此时对于糖果的数组来说,每一个位置都是满足右边的大于左边的糖果数,但是此时如果对于左边的大于右边的分数时,我们简单的让v[i] = v[i+1] + 1;那么这一步是不对的,因为他会破坏第一个条件,所以我们需要做的就是,两个中取较大值。
class Solution {
public:
    //两个局部最优,求全局最优,就可以得到我们想要的结果
    int candy(vector<int>& ratings) {
        int n = ratings.size();
        vector<int> v(n,1);
        //如果右边的比左边的大,那么就要给右边多加一个
        for(int i = 1;i<n;++i)
        {
            if(ratings[i] > ratings[i-1])
                v[i] = v[i-1] +1;
        }
        //从右往左进行遍历的过程需要好好的思考和调整
        for(int i = n-2;i>=0;--i)
        {
            //这里为啥要去最大值呢?
            //因为此时数组里面的糖果数量已经满足了大于左边,那么如果我们只是单纯的考虑右边进行修改
            //那么左边就不再满足了,所以我们取既满足左边,又满足右边的一个数
            if(ratings[i] > ratings[i+1])
                v[i] = max(v[i],v[i+1] + 1);
        }
        int res = 0;
        for(int& num : v)
            res += num;
        return res;
    }
};

9. LeetCode第55题—跳跃游戏I

LeetCode题目链接:https://leetcode-cn.com/problems/jump-game/
在这里插入图片描述
就看能否覆盖的面积达到数组的最后一个位置。

class Solution {
public:
    bool canJump(vector<int>& nums) {
        //每一个下标表示你可以跳跃的最大长度,但是并不表示你必须要跳跃这么大才可以
        //在所能移动的数组中不断的更新我想要的最优结果
        //起始对于这道题我们并不需要关心他一次跳跃几步,能跳到哪里,我们更多所需要去关心的就是,他所能覆盖的面积,是否能够达到数组的最后一个位置。
        int cover = nums[0];
        for(int i = 0;i<=cover;++i)
        {
            cover = std::max(i+nums[i],cover);
            if(cover >= nums.size()-1)
                return true;
        }
        return false;
    }
};

9.1 LeetCode第55题—跳跃游戏II

LeetCode题目链接:https://leetcode-cn.com/problems/jump-game-ii/
在这里插入图片描述
那么这道题和第一道跳跃游戏究竟有什么不同呢?
其实这是一道贪心算法+动态规划的题目

  1. 如果当前覆盖最远距离下标不是是集合终点,步数就加一,还需要继续走。
  2. 如果当前覆盖最远距离下标就是是集合终点,步数不用加一,因为不能再往后走了。(所以条件判断里面才会写nums.size()-1)
class Solution {
public:
    int jump(vector<int>& nums) {
        int maxfar = 0;//下一步所能覆盖的最远位置下标
        int curfar = 0;//当然所能覆盖的最远位置下标
        int step = 0;
        //这里为什么是nums.size()-1需要好好的思考一下
        //因为即使在做一次,很有可能依旧会在这个位置卡住
        //如果这里让i等于最后一个位置下标,那么毫无争议的可以说,次数就会多增加一次
        for(int i = 0;i<nums.size()-1;++i)
        {
            maxfar = max(maxfar,i+nums[i]);
            if(i == curfar)
            {
                curfar = maxfar;
                step++;
            }
        }
        return step;
    }
};

10. LeetCode第738题—单调递增的数字

LeetCode题目链接:https://leetcode-cn.com/problems/monotone-increasing-digits/
在这里插入图片描述
例如:98,一旦出现strNum[i - 1] > strNum[i]的情况(非单调递增),首先想让strNum[i - 1]–,然后strNum[i]给为9,这样这个整数就是89,即小于98的最大的单调递增整数。

这一点如果想清楚了,这道题就好办了。

局部最优:遇到strNum[i - 1] > strNum[i]的情况,让strNum[i - 1]–,然后strNum[i]给为9,可以保证这两位变成最大单调递增整数。

class Solution {
public:
    //局部最优求解全局最有的一种方式,从后向前进行遍历,
    int monotoneIncreasingDigits(int N) {
        string strNum = to_string(N);
        // flag用来标记赋值9从哪里开始
        // 设置为这个默认值,为了防止第二个for循环在flag没有被赋值的情况下执行
        int flag = 0;
        for (int i = strNum.size() - 1; i > 0; i--) {
            //如果此时不如"98"
            if (strNum[i - 1] > strNum[i] ) {
                flag = i;
                strNum[i - 1]--;
            }
        }
        for (int i = flag; i < strNum.size(); i++) {
            strNum[i] = '9';
        }
        return stoi(strNum);
    }
};

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值