两个维度一定先确定一个维度,再判断另一个维度
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、有三种状态,该节点无覆盖、本节点有摄像头、本节点有覆盖
空节点不能是无覆盖状态状态,这样需要放摄像头
空节点不能是有摄像头状态状态,这样叶子节点的爹就不用放摄像头了,所以叶子空节点是无覆盖状态。