860.柠檬水找零
文章讲解:https://programmercarl.com/0860.%E6%9F%A0%E6%AA%AC%E6%B0%B4%E6%89%BE%E9%9B%B6.html
题目链接:https://leetcode.cn/problems/lemonade-change/
视频讲解:https://www.bilibili.com/video/BV12x4y1j7DD/
每一次会收进来钱,如果是5的话就不用找零,如果不是5的话就要找零,会得到一个map key是钱 value是张,局部最优,每次找零先拿最大的找,找完再用小的,后面是在想不出来贪心的逻辑,看了下代码随想录。
对题目分情况考虑,题目分三种情况:
情况一:遇到5,不用找零,增加一个5。
情况二:遇到10,消耗5,增加一个10。
情况三:遇到20,优先消耗一个10和一个5,如果不够,再消耗三个5。
这里的贪心:因为美元10只能给账单20找零,而美元5可以给账单10和账单20找零,美元5更万能!。
所以局部最优:遇到账单20,优先消耗美元10,完成本次找零。全局最优:完成全部账单的找零。
分情况考虑之后逻辑更加简单,这里优先消耗10美元之前自己也有想到,但是贪心算法的逻辑实在是太像脑筋急转弯。
这道题目可以告诉大家,遇到感觉没有思路的题目,可以静下心来把能遇到的情况分析一下,只要分析到具体情况了,一下子就豁然开朗了。
如果一直陷入想从整体上寻找找零方案,就会把自己陷进去,各种情况一交叉,只会越想越复杂了。
public boolean lemonadeChange(int[] bills) {
int[] money = new int[2];
for(int i = 0; i < bills.length; i++){
// 给的是5的情况,直接加一
if(bills[i] == 5){
money[0]++;
}else if(bills[i] == 10){
// 给的是10的情况,要用5去找零
if(money[0] > 0){
// 表示有5
money[0]--;
money[1]++;
}else{
return false;
}
}else{
// 给的是20的情况,优先用10,再用5
if(money[1] > 0){
// 有10的情况,
if(money[0] > 0){
// 有5的情况
money[1]--;
money[0]--;
}else{
// 没5的情况,
return false;
}
}else{
// 没10得情况,都用5
if(money[0] >= 3){
money[0]-=3;
}else{
return false;
}
}
}
}
return true;
}
406.根据身高重建队列
文章讲解:https://programmercarl.com/0406.%E6%A0%B9%E6%8D%AE%E8%BA%AB%E9%AB%98%E9%87%8D%E5%BB%BA%E9%98%9F%E5%88%97.html
题目链接:https://leetcode.cn/problems/queue-reconstruction-by-height/
视频讲解:https://www.bilibili.com/video/BV1EA411675Y/
刚开始想尝试分发糖果的逻辑,只处理一边,但是看到二维数组就想不到该如何处理了。
代码随想录的逻辑就是遇到两个维度权衡的时候,一定要先确定一个维度,再确定另一个维度。
这里的困惑点就是优先维度的选择,是先确定k还是先确定h。这里按照k从小到大排序:
如people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]] 按照k排序,从小到大排,得[5,0],[7,0],[6,1],[7,1],[5,2],[4,4],这里[5,0],[7,0],[6,1],[7,1]还算正常,但是到了[5,2]和[4,4]都不符合,k的值不对。h的值也没确定下来。k的排列并不符合条件,身高也不符合条件,两个维度哪一个都没确定下来。所以不用k来排序。
按照身高h来排序的话,则身高一定是从大到小排(身高相同的话则k小的站前面),让高个子在前面。
此时我们可以确定一个维度了,就是身高,前面的节点一定都比本节点高!
排完序后,优先把身高高的people插入到新数组最前,后遍历到的值按照k来进行插入,后插入的如果k为0则插到新数组最前。
按身高排完序后: [[7,0],[7,1],[6,1],[5,0],[5,2],[4,4]]
局部最优:优先按身高高的people的k来插入。插入操作过后的people满足队列属性。
全局最优:最后都做完插入操作,整个队列满足题目队列属性。
代码随想录的插入逻辑很简单,直接Linkedlist.add(index, value)进行插入,其中index直接使用k,循环排好序的序列,然后按照k的序列做插入。
二维数组的排序,数组的插入,操作比较复杂就没写了。
没想到插入逻辑可以直接使用链表做插入并且k的值直接作为index插入。
452. 用最少数量的箭引爆气球
文章讲解:https://programmercarl.com/0452.%E7%94%A8%E6%9C%80%E5%B0%91%E6%95%B0%E9%87%8F%E7%9A%84%E7%AE%AD%E5%BC%95%E7%88%86%E6%B0%94%E7%90%83.html
题目链接:https://leetcode.cn/problems/minimum-number-of-arrows-to-burst-balloons/
视频讲解:https://www.bilibili.com/video/BV1SA41167xe/
找到有重叠的组数量,重叠组的数量即为最小弓箭数
局部最优:每次射箭都射在最大重叠处。全局最优,重叠地方射箭数量合最少。
求重叠后的数组数量, 先进行排序,按照start排序,双层循环找出重叠最多的组合
局部最优:当气球出现重叠,一起射,所用弓箭最少。全局最优:把所有气球射爆所用弓箭最少。
如果气球重叠了,重叠气球中右边边界的最小值之前的区间一定需要一个弓箭。
以题目示例: [[10,16],[2,8],[1,6],[7,12]]为例,先排序后得到[1,6],[2,8],[7,12],[10,16]。
1-6和2-8,则需要将2-8的最右区间改为6,以6作为下一个比较区间的范围。
如果第一个range的最右边和第二个range的最左边没交集,则直接射一次箭。
看完代码随想录文章后自己实现没有问题,核心还是要思考到用最右边界的思路。排序的逻辑其实已经想到了,但是没想到最右边界的逻辑和直接射箭的逻辑。
public int findMinArrowShots(int[][] points) {
// 找到有重叠的组数量,重叠组的数量即为最小弓箭数
// 局部最优:每次射箭都射在最大重叠处。全局最优,重叠地方射箭数量合最少。
// 求重叠后的数组数量
// 先进行排序,按照start排序
Arrays.sort(points, Comparator.comparingInt(a -> a[0]));
int count = 1;
for(int i = 1; i < points.length; i++){
// 如果第一个range的最右边和第二个range的最左边没交集,则直接射一次箭
if(points[i - 1][1] < points[i][0]){
count++;
}else{
points[i][1] = Math.min(points[i - 1][1],points[i][1]);
}
}
return count;
}
学习时长:2小时以上
今日收获:贪心还是比较难想到,只能抓到一丝想法,都是看了代码随想录写出来的。
今日的收获主要分三块: