贪心算法练习题二

两个维度一定先确定一个维度,再判断另一个维度

1、分发糖果

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。

你需要按照以下要求,帮助老师给这些孩子分发糖果:

每个孩子至少分配到 1 个糖果。
相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?

输入: [1,0,2]
输出: 5
解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。

分发糖果
c++版本

class Solution {
public:
    int candy(vector<int>& ratings) {
        /* 从左向右比较,如果右边比左边大,让右边为+1,否则为1
            从右向左比较,如果左边比右边大,让左为+1,否则为1
            取两者的最大值
        */
        if (ratings.size() == 1) return 1;
        //由于都涉及1,因此开始时将数组元素都设置为1
        vector<int> toright(ratings.size(), 1);
        vector<int> toleft(ratings.size(), 1);
        //从左向右
        for (int i = 1; i < ratings.size(); i++)
        {
            if (ratings[i - 1] < ratings[i]) toright[i] = toright[i - 1] + 1;
        }
        //从右向左
        for (int i = ratings.size() - 2; i >= 0; i--)
        {
            if (ratings[i] > ratings[i + 1]) toleft[i] = toleft[i + 1] + 1;
        }
        //取两个数组的最大值
        int ret = 0;
        for (int i = 0; i < ratings.size(); i++)
        {
            ret += max(toright[i], toleft[i]);
        }
        return ret;
    }
};

2、柠檬水找零

在柠檬水摊上,每一杯柠檬水的售价为 5 美元。

顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。

每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。

注意,一开始你手头没有任何零钱。

如果你能给每位顾客正确找零,返回 true ,否则返回 false 。
在这里插入图片描述
在这里插入图片描述
简单if判断

406.根据身高重建队列

假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。

请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。

在这里插入图片描述

遇到两个维度权衡的时候,一定要先确定一个维度,再确定另一个维度。如果两个维度一起考虑一定会顾此失彼。
那么是先确定身高,还是先确定前面人的个数呢。
如果按人的个数排序,排完后发现人的排列不符合要求,如下图所示,0 0 2后面又出现小于2的数1
在这里插入图片描述

如果按身高排序,身高矮的可以放到前面也可以身高高的放前面(相同身高按第二个值个数来衡量),我们先按身高高的放在前面,这样就可以排,然后按第二个值来重新排列,由于升高高的在前面,后面矮的移动到前面不会影响自己,因此成立
在这里插入图片描述

因此需要按照身高排序,所以可以确定维度了,那就是身高
身高排完了以后再按照第二个值身高的人数来排列。

452、用最少数量的箭引爆气球

在这里插入图片描述

在这里插入图片描述
先按第一个值排序,然后以重叠为射箭
在这里插入图片描述
在这里插入图片描述
如果仅判断下边的值是否是在我的右边界以内,有可能出现下面的情况
在这里插入图片描述
重新整理
在这里插入图片描述
在这里插入图片描述
两两进行比较,后面的和前面的进行比较,如果不挨着,说明前面需要一个箭,
如果挨着,那么我进入前面的队列,也就是我和前面的绑到一块用一根箭,那么我应该是在前半块被包含的那一块地方被射中,所以要更新重叠气球的最小右边界
在这里插入图片描述

435. 无重叠区间

给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。

输入: intervals = [[1,2],[2,3],[3,4],[1,3]]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。

首先想到的思路:
排序:按照左边界排序,左边界相同,右边界小的在前面
遍历:左边界相同的 ,右边界小的留着(因为有更小机会和别的区间相撞)
但是这样遇到一个问题,当左边界不同时两个区间重叠了怎么办,难道是两个区间取小的?
记录:本想着维护一个最小区间,来记录选择的区间的个数并和下面做比较,作比较可以直接两两比较不需要维护一个最小区间。

上面一会验证一下。
下面研究下答案的思路。
排序:如果按左边界排序那么就从右向左排序,如果按右边界排序,那么就从左向右排序
**遍历:**以左向右排序为例,维护一个end和count记录不重叠区域,当下一个区间的开头在我end里面,那么就是重叠的,不要他,否则就以它end为end。
为什么可以这样?
由于是以右边界排序,从左遍历,首先碰到的肯定是右边界小的那个,那么它向后延伸的就小。

验证刚开始的思路
出现问题
在这里插入图片描述
加粗样式
为什么会有问题,因为在时空上前面的已经没有影响了,但是选择后面的会有别的区间相撞。
那么按照正确答案上由于前面已经确定了,那么越往后的越容易相撞

已经确定了一个边,不会像上一个思路一样和没有确定比较的一样。正常推理,在开始的时候,由于尾相同,那么这样是对的
在这里插入图片描述
根据第一个推第二个
在这里插入图片描述

class Solution {
public:
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if(intervals.size() == 1) return 0;
        sort(intervals.begin(),intervals.end(),[](vector<int>&a,vector<int>&b){
            if(a[1] == b[1]) return a[0] < b[0];
            return a[1] < b[1];
        });   
        int end = intervals[0][1]; 
        int count = 0;
        for(int i = 1; i <intervals.size(); i++){
            if(end <= intervals[i][0]) //不重叠
             end = intervals[i][1];
            else count ++;
        }

        return count;

    }
};

763. 划分字母区间

字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。

输入:S = “ababcbacadefegdehijhklij”
输出:[9,7,8]
解释:
划分结果为 “ababcbaca”, “defegde”, “hijhklij”。
每个字母最多出现在一个片段中
像 “ababcbacadefegde”, “hijhklij” 的划分是错误的,因为划分的片段数较少。

一个字母只能在一个段里面,把相同的字母划到相同的段里面。划分的字段尽可能多。

和区间划分一个道理 ,每个字母要划分一个范围空间

首先的思路是将abcbabe找每个元素的最大区域,用一个unordered_map<char, vector< int >>存放着,修改原串为abc0abe,然后就是划分区间了 ,但是在区间排序的时候特别啰嗦
在这里插入图片描述
答案的意思是不需要找开头节点,只需要找最终节点,由于都是要遍历的,所以不需要当前节点
在这里插入图片描述

在这里插入图片描述

56. 合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
在这里插入图片描述
在这里插入图片描述

这个题只能是左边界排序,然后就从左向右来

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        //排序
        sort(intervals.begin(), intervals.end(), [](vector<int>& a, vector<int>& b) {
            return a[0] < b[0];
            });
        int left = intervals[0][0];
        int right = intervals[0][1];  
        vector< vector<int>>ret;
        for (int i = 1; i < intervals.size(); i++) {
            if (right < intervals[i][0]) {
                vector<int>temp{ left,right };
                ret.emplace_back(temp);
                left = intervals[i][0];
                right = intervals[i][1];
            }
            else right = max(right, intervals[i][1]);       
        }
        vector<int>temp{ left,right };
        ret.emplace_back(temp);
        return ret;
    }
};

无重叠区间和合并重叠区间对比。

合并重叠区间碰到重叠只需要更新scope就行了,重叠就判断哪个右边界大
无重叠区间碰到重叠的是要想删哪个,这个没有标准,需要确定一个边界,然后才能考虑删除哪个,也就是爪子伸到我空间了,我就可以删掉它
在这里插入图片描述

738. 单调递增的数字

当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。
给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。

在这里插入图片描述
不能仅单纯的从后面向前面去判断是否是这样并修改数
在这里插入图片描述
在这里插入图片描述

714. 买卖股票的最佳时机含手续费

在买卖时需要有手续费

输入:prices = [1, 3, 2, 8, 4, 9], fee = 2
输出:8
解释:能够达到的最大利润:
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8

去他妹的,这个用动态规划才是正道,才是常规做法

968. 监控二叉树

给定一个二叉树,我们在树的节点上安装摄像头。

节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。

计算监控树的所有节点所需的最小摄像头数量。
在这里插入图片描述
发现头或者尾都不会放摄像头,可以从头或者尾开始,但是头上不放摄像头只会省1个,但是尾不放摄像头会省指数级的个数。因此从尾开始。
大体思路就是从低到上,先给叶子节点父节点放个摄像头,然后隔两个节点放一个摄像头,直至到二叉树头结点。
如何:1、二叉树的遍历 2、如何隔两个节点放一个摄像头
1、使用递归,左右中,就是从后向前遍历
2、有三种状态,该节点无覆盖、本节点有摄像头、本节点有覆盖
空节点不能是无覆盖状态状态,这样需要放摄像头
空节点不能是有摄像头状态状态,这样叶子节点的爹就不用放摄像头了,所以叶子空节点是无覆盖状态。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

贪睡的蜗牛

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

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

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

打赏作者

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

抵扣说明:

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

余额充值