什么是贪心选择性质呢,简单说就是:每一步都做出一个局部最优的选择,最终的结果就是全局最优。注意哦,这是一种特殊性质,其实只有一小部分问题拥有这个性质。
比如你面前放着 100 张人民币,你只能拿十张,怎么才能拿最多的面额?显然每次选择剩下钞票中面值最大的一张,最后你的选择一定是最优的。
然而,大部分问题都明显不具有贪心选择性质。比如打斗地主,对手出对儿三,按照贪心策略,你应该出尽可能小的牌刚好压制住对方,但现实情况我们甚至可能会出王炸。这种情况就不能用贪心算法,而得使用动态规划解决,参见前文 动态规划解决博弈问题。
https://leetcode-cn.com/problems/non-overlapping-intervals/submissions/
leetcode435
贪心问题 ,先排顺序,按照终止时间排序
再统计不相交的时间,
再做差
class Solution {
// 贪心策略,先计算最多能组成的不重叠区间个数,然后用区间总个数减去不重叠区间的个数。
public int eraseOverlapIntervals(int[][] intervals) {
if(intervals==null || intervals.length<=0)return 0;
int n = intervals.length;
//返回的是 该去除的区间数
return n-count_no_jiaoji(intervals);
}
//统计几个区间 时间是不相交
int count_no_jiaoji(int[][] intervals){
if(intervals==null || intervals[0].length<=0)return 0;
//按照stop 时间重新排序
Arrays.sort(intervals,new Comparator<int []>(){
@Override
public int compare(int []a1,int a2[]){
return a1[1]-a2[1];
}
});
int count = 1;// 至少有一个 区间不相交
int x_end = intervals[0+0][0+1];
for(int []inter:intervals){
//每个区间进行计算 ,每个区间 的起始时间
int start = inter[0];
if(x_end<=start){
count++;
x_end = inter[1];
}
}
//返回的是不相交的区间数目 。
return count;
}
}
打气球问题
leetcode452
class Solution {
public int findMinArrowShots(int[][] points) {
if(points==null || points.length<=0)return 0;
int len = points.length;
return findNochongdie(points);
}
int findNochongdie(int[][] points){
Arrays.sort(points,new Comparator<int[]>(){
//按照stop 时间重新排序
@Override
public int compare(int []a1,int a2[]){
// [[-2147483646,-2147483645],[2147483646,2147483647]]
// return Integer.compare(a1[1],a2[1]);
return a1[1]>a2[1]?1:-1;
}
});
int count = 1;// 至少有一个 区间不相交
int x_end = points[0+0][0+1];
for(int []inter:points){
//每个区间进行计算 ,每个区间 的起始时间
int start = inter[0];
if(x_end< start){
count++;
x_end = inter[1];
}
}
//返回的是不相交的区间数目 。
return count;
}
}
经典贪心问题
跳格子
leetcode 55
class Solution {
public boolean canJump(int[] nums) {
int len = nums.length;
int max = nums[0];
if(len<=1)return true;
for(int i=1;i<len-1;i++){
// if(max)
//不断更新
if(i<=max){
max = Math.max(max,nums[i]+i);
}
}
return max>=(len-1) ? true : false ;
}
}
删除被覆盖的区间
leetcode 1288
排序
public int removeCoveredIntervals(int[][] intervals) {
Arrays.sort(intervals,(a,b)->{
if(a[0]==b[0]) {return b[1] - a[1];}
return a[0] - b[0];
});
int left = intervals[0][0];
int right = intervals[0][1];
int res = 0;
for (int i = 1+0; i < intervals.length; i++) {
int[] intv = intervals[i];
// 情况一,找到覆盖区间
if (left <= intv[0] && right >= intv[1]) {
res++;
}
// 情况二,找到相交区间,合并
if (right >= intv[0] && right <= intv[1]) {
right = intv[1];
}
// 情况三,完全不相交,更新起点和终点 ,left right 都更新
if (right < intv[0]) {
left = intv[0];
right = intv[1];
}
}
return intervals.length - res;
}
跳跃问题
自顶向下的方法。
class Solution {
public int jump(int[] nums) {
int len =nums.length;
int memo[] = new int[len];
for(int i=0;i<len;i++){
memo[i] = len;//至多跳跃 n -1 步
}
return dp( nums,0,memo);
}
//从 index 处跳到 结尾 len-1 的所用的步数
int dp(int []nums,int index,int[]memo){
//终止条件达到 了索引 的最后
int len = nums.length;
if(index>=len-1){
return 0;
}
//该位置已经计算过了
if(memo[index]!=len){return memo[index];}
int step = nums[index];
for(int i=1;i<=step;i++ ){
//穷举所有的跳跃
int sub= dp(nums,index+i,memo);
memo[index] = Math.min(sub+1,memo[index])
; }
return memo[index];
}
}