【4.25-4.29周一到周五打卡】贪心问题

4.25-周一

1005.K次取反后最大化数组和

链接直达

image-20220429205242823

思路分析

  • 对某一个点进行取反操作,那么我们需要注意:同一个位置取反,k为奇数,才起到效果,k为偶数时,相当于没有取反
  • 需要求得最大和,如果有负数,那么可以将负数取反,达到sum变大得效果。如果是正数,那么考虑取反最小的正数

代码

class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        Arrays.sort(nums);
        //遍历数组,将负数全部取反
        int m = 0;
        for(int i = 0; i < nums.length; i++){
            if(nums[i] < 0 && k > 0){
                nums[i] = -nums[i];
                m++;
                k--;
            }
        }
        //k > 0 需要将剩下数组中正数取反了
        if(k > 0){
            if(k % 2 == 1){
                //表明k为奇数,反转奇数次才会为负数
                
                //因为之前数组排序过,所以此时数值最小的非负数是num[m]或者num[m - 1]
                if(m == 0){
                    nums[m] = -nums[m];
                }else if(m < nums.length){
                            if(nums[m] > nums[m - 1]) nums[m - 1] = -nums[m - 1];
                            else nums[m] = -nums[m];
                        }else if(m == nums.length){
                            nums[m-1] = -nums[m - 1];
                        }
            }
        }
        int sum = 0;
        for(int j = 0; j < nums.length; j++){
             sum += nums[j];
        }
        return sum;
    }
}

134.加油站

链接直达

image-20220429205726187

思路分析

  • 首先我们已知,当途径全程可以获得的gas数值和如果大于我们全程消耗的cost数值和,这个时候是可以形成环路的。这个挺生活常识的,就是够用,因为我们不知道从哪开始出发嘛

  • 关键点就在于寻找从哪个站出发了,实际上从这个站出发,就是为了填补前面的不够的油量。比如三站

    gas【4,2,3】 cost【1,1,10】。从前面两站出发,明显是油量不够的。从第三站出发10-3,还剩7,然后7-4还剩3,就算不补充油量,也足够了。

  • 实际上核心就是,在什么地方出发,我们可以填补前面的油量不够

代码

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int index = 0;
        int spare = 0;
        int minSpare = 100000;
        for(int i = 0; i < gas.length; i++){
            spare += gas[i];
            spare -= cost[i];
            if(spare < minSpare){
                minSpare = spare;
                index = i;
            }

        }
        return spare < 0 ? -1 : (index + 1) % (gas.length);
    }
}

4.26-周二

135.分发糖果

链接直达

image-20220429210753798

思路分析

  • 左右遍历模拟值。首先想到的就是每一个同学都发一颗糖。
  • 按照题目描述,相邻孩子评分高的会获得更多的糖果,那么从左向右遍历,如果右边的同学比左边的同学评分多,那么就加一颗糖。这一点我们很容易想到,但是还远远不够
  • 如果我们的孩子是【2,2,1】,首先每个孩子都可以拿一颗糖,但是都不满足右边的孩子比左边的孩子评分高,答案是不是3呢,显然不是,因为第二个孩子和第三个孩子都为一颗糖就不满足题意了
  • 所以我们不仅仅要左遍历一遍,还需要右遍历一遍,然后取最大值,这样的话就能满足条件了

代码

class Solution {
    public int candy(int[] ratings) {
        int [] left = new int[ratings.length];
        int [] right = new int[ratings.length];
        Arrays.fill(left, 1);
        Arrays.fill(right, 1);
        for(int i = 1; i < ratings.length; i++){
            if(ratings[i] > ratings[i - 1]){
                left[i] = left[i - 1] + 1;
            }
        }
        int count = 0;

        for(int i = ratings.length - 2; i >=0; i--){
            if(ratings[i] > ratings[i + 1]){
                right[i] = right[i + 1] + 1;
            }
            count += Math.max(left[i], right[i]);
        }
        //right数组开始的范围是[0,ratings.length - 2];在这次比较重,我们没有加上left的最后一个元素,也就是ratings.length - 1的位置
        count += left[ratings.length - 1];

        return count;
    }
}

860.柠檬水找零

链接直达

image-20220429210821145

思路分析

  • 非常舒服的简单题
  • 贪心思想:能不用五元就不用五元,因为五元很通用,简单模拟交易钱币过程就好

代码

class Solution {
    public boolean lemonadeChange(int[] bills) {
        int ten = 0;
        int five = 0;
        for(int i = 0; i < bills.length; i++){
            if(bills[i] == 5){
                five++;
            }else if(bills[i] == 10){
                five--;
                ten++;
            }else{
                if(ten > 0){
                    ten--;
                    five--;
                }else{
                    five = five - 3;
                }

            }
            if(five < 0 || ten < 0){
                return false;
            }
        }
        return true;
    }
}

4.27-周三

406.根据身高重建队列

链接直达

image-20220429210857135

思路分析

  • 按照题意来说,其实就是按照身高排序,但是也不完全按照身高排序,真正的排序因子,还是people[i][1]
  • 那么首先我们按照升高排序,然后再通过people[i][1]来调整顺序就好了
  • 调整顺序的操作通过值得大小顺序插入就行

代码

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        Arrays.sort(people, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                if(o1[0] != o2[0]){
                    //降序
                    return o2[0] - o1[0];
                }else{
                    //升序
                    return o1[1] - o2[1];
                }
            }
        });
        
        List<int[]> arr = new ArrayList<>();
        for(int i = 0; i < people.length; i++){
            if(arr.size() > people[i][1]){
                arr.add(people[i][1],people[i]);
            }else{
                arr.add(arr.size(),people[i]);
            }
        }
        return arr.toArray(new int[arr.size()][]);

    }
}

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

链接直达

image-20220429210940336

思路分析

  • 很容易想到排序

画个图:

image-20220430140811196

  • 题目要求使用最少的箭,贪心:思考什么时候我们才不得已需要加一支箭
  • 已知第一根箭是【1,6】,只要后面的气球范围第一个数如果是小于6,那么就说明这可以和【1,6】用于一根箭,那么就是说,只有气球范围【start,end】中,start大于6,才需要加一根箭
  • 也就是【7,12】需要一根箭,此时的问题就在后续的气球是否可以和【7,12】共有箭呢?那么将箭的范围更新至12处

代码

class Solution {
    public int findMinArrowShots(int[][] points) {
        if(points.length == 0){
            return 0;
        }
        Arrays.sort(points, new Comparator<int[]>(){
            public int compare(int[] point1, int[] point2){
                if(point1[1] > point2[1]){
                    return 1;
                }else if(point1[1] < point2[1]){
                    return -1;
                }else{
                    return 0;
                }
            }
        });
        int pos = points[0][1];
        int sum = 1;
        for(int[] ball : points){
            if(ball[0] > pos){
                pos = ball[1];
                sum++;
            }
        }
        return sum;
    }
}

4.28-周四

435.无重叠区间

链接直达

image-20220429211026476

思路分析

  • 这题和昨天的射击气球还是有一点相似的

  • 依旧是很容易想到了先排序

    画个图:

    image-20220430141634368

  • 贪心:思考什么情况下,才需要移除一个区间,达到没有重复区间

  • 和射气球原理一样,当发现重复区间的时候将count++,表示需要删除这个区间,那么删除区间之后需要更新区间的临界值【pos】,删除的个数要保障最少,那么我们取那个占用区间多的,比如【1,3】如果我们删除它就只用删除一个,但是如果删除【1,2】,那么我们后续【2,3】和【1,3】还是重合,这样就需要删除两个了。那么更新区间临界值pos的时候,采用pos = Math.min(intervals[i][1], pos);

  • 当没有重复区间的时候,更新pos,只需要按照范围移动就好了pos = intervals[i][1];

代码

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        if(intervals.length == 0){
            return 0;
        }
        Arrays.sort(intervals, new Comparator<int[]>(){
            public int compare(int[] intervals1, int[] intervals2){
                if(intervals1[1] > intervals2[1]){
                    return 1;
                }else if(intervals1[1] < intervals2[1]){
                    return -1;
                }else{
                    return 0;
                }
            }
        });
        int pos = intervals[0][1];
        int count = 0;
        for(int i = 1; i < intervals.length; i++){
            if(intervals[i][0] < pos){
                count++;
                pos = Math.min(intervals[i][1], pos);
            }else{
                pos = intervals[i][1];
            }
        }
        return count;
    }
    
}

4.29-周五

763.划分字母区间

链接直达

image-20220429214131234

思路分析

  • 使用一个额外数组来存储每一个字母的出现的最大下标值。
  • 比如【ababa】 —> a出现的最大下标是4,b是3,那么对应ans就是这样的映射关系。
  • 然后遍历由s转化的char数组,当达到某个字母的最大下标的时候,就更新分割点

代码

class Solution {
    public List<Integer> res = new ArrayList<>();
    public List<Integer> partitionLabels(String s) {
        int [] ans = new int[26];
        char[] chars = s.toCharArray();
        for(int i = 0; i < chars.length; i++){
            ans[chars[i] - 'a'] = i;
        }
        int index = 0;
        int last = 0;
        for(int i = 0; i < chars.length; i++){
            index = Math.max(index, ans[chars[i] - 'a']);
            if(i == index){
                res.add(i - last + 1);//下标相减,求字母个数,所以+1
                last = i + 1;//更新下标
            }
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值