贪心问题没有特别多的套路,大多数思想就是模拟,简单来说就是看到题目觉得不用特别的算法,通过数学归纳法做能行,就采用贪心问题。本文就对相关的贪心问题进行汇总,也把一些类似的思想归下类,供大家学习参考。
本文参考:
基础知识
「贪心算法」的问题需要满足的性质:
- 最优子结构:规模较大的问题的解由规模较小的子问题的解组成,规模较大的问题的解只由其中一个规模较小的子问题的解决定;
- 无后效性:后面阶段的求解不会修改前面阶段已经计算好的结果;
- 贪心选择性质:从局部最优解可以得到全局最优解。
相关题目
1710.卡车上的最大单元数
先贪心排序,然后放箱子,其中放箱子的过程中,通过for循环中的i<n&&cnt<k
控制循环,同时在内部通过c=Math.min(a,k-cnt)
来得出当前能放的该类型最多的箱子,是min(该箱子总数,当前我还能放的数目),这种简洁的写法值得学习。
class Solution {
public int maximumUnits(int[][] bs, int k) {
// 将能装载最大单元数量的箱子放在前面
Arrays.sort(bs,(a,b)->b[1]-a[1]);
int n=bs.length,ans=0;
// 遍历所有箱子,直到放满k
for(int i=0,cnt=0;i<n&&cnt<k;i++){
int a=bs[i][0],b=bs[i][1],c=Math.min(a,k-cnt);
cnt+=c;
ans+=c*b;
}
return ans;
}
}
区间问题
心得体会:
- 在求无重叠区间需要移除的个数时候会从反面考虑,考虑比较好算的无重叠区间有多少个,最后用总数减掉,也不关心重叠区间应该如何删除,当碰到重叠的时候,也只是更新最小右边界。
- 在求重叠区间的时候都会进行排序,有些题目用来求重叠区间最小数量,如646.最长数对链,有些是来求重叠区间最大数量,如452.用最少数量的箭引爆气球。两面理解起来,排序以后维护最小右边界,不重叠区间最多是通过使右边界最小实现的,而重叠区间最多也是通过尽可能在两个气球右边界的最小值射箭实现的。
435.无重叠区间
这道题要尽可能地在一个时间段内安排多个课程,直观的贪心思想。先排序,然后根据排序结果贪心,尽可能多安排区间分散开来,移除区间的数目就是区间总数目减去不重叠区间的数目。求移除区间数目,可以根据左边界升序排序也可以根据右边界升序排序。
左边界升序排序,参考用引爆气球贪心思想解决区间问题「图解」,其中在原数据上维护更新重叠区间的最小右边界思想值得学习,其中result求的是「不重叠区间的数目」。移除区间的数目就是区间总数目减去不重叠区间的数目,不重叠区间的数目是理论上来说最后会留下来的那些区间,维护更新重叠区间的最小右边界时优先选择重叠的较小边界,从而给后面的区间腾空间,也相当于是多了一个需要移除的区间。
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
int len=intervals.length;
Arrays.sort(intervals,(o1,o2)->o1[0]-o2[0]);
//intervals[0]是最终答案里的首个元素
int res=1;
for(int i=1;i<len;i++){
//不重叠区间
if(intervals[i][0]>=intervals[i-1][1]) res++;
else{
//重叠区间更新右边界
intervals[i][1]=Math.min(intervals[i-1][1],intervals[i][1]);
}
}
return len-res;
}
}
右边界升序排列,参考贪心解法其实就是一层窗户纸,戳破了就好了!,同样是找数量最多的不重叠区间,尽可能每次找到最小的右边界,不过这里和上面的区别就是没有针对重叠区间进行更多逻辑处理。
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
int len=intervals.length;
Arrays.sort(intervals,(o1,o2)->o1[1]-o2[1]);
//intervals[0]是最终答案里的首个元素
int res=1;
//右边界升序,维护一个变量right
int right=intervals[0][1];
for(int i=1;i<len;i++){
//与前面区间不重叠的区间>=right
if(intervals[i][0]>=right){
res++;
right=intervals[i][1];
}
}
return len-res;
}
}
646.最长数对链
这题直接求的是不重叠区间,解法类似上面
class Solution {
public int findLongestChain(int[][] pairs) {
int len=pairs.length;
Arrays.sort(pairs,(o1,o2)->o1[0]-o2[0]);
int res=1;
for(int i=1;i<len;i++){
if(pairs[i][0]>pairs[i-1][1]){
res++;
}else{
pairs[i][1]=Math.min(pairs[i-1][1],pairs[i][1]);
}
}
return res;
}
}
452.用最少数量的箭引爆气球
这题类似646.最长数对链,只要重叠区间越多,则箭的数量越少。但是同样的做法,上面是求不重叠区间的数量最多,这里是求重叠的数量最多,在最多之中蕴含着贪心。两面理解起来,排序以后维护最小右边界,不重叠区间最多是通过使右边界最小实现的,而重叠区间最多也是通过尽可能在两个气球右边界的最小值射箭实现的。
class Solution {
public int findMinArrowShots(int[][] points) {
int len=points.length;
Arrays.sort(points,(o1,o2)->Integer.compare(o1[1],o2[1]));
int end=points[0][1];
int res=1;
for(int i=1;i<len;i++){
//求不重叠区间数目res
if(points[i][0]>end){
res++;
end=points[i][1];
}
}
return res;
}
}