Leetcode weekly contest176 1351-1354

1、每行每列的数都是都是递减的,求负数有多少个

 

O(M+N):

    public int countNegatives(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int x = m - 1;
        int y = 0;
        int res = 0;
        while (x >= 0) {
            while (y < n && grid[x][y] >= 0) y++;
            res += n - y;
            x--;
        }
        return res;
    }

 

2、前缀积,注意到0的情况,需要分段处理

    int n;
    List<Integer> list;
    List<Integer> zero;

    public ProductOfNumbers() {
        n = 0;
        list = new ArrayList<>();
        list.add(1);
        zero = new ArrayList<>();
    }

    public void add(int num) {
        n++;
        if (num == 0) {
            zero.add(n);
            list.add(0);
        } else {
            int last = list.get(n - 1);
            if (last == 0) list.add(num);
            else list.add(last * num);
        }
    }

    public int getProduct(int k) {
        int pre = n - k;
        for (int x : zero) {
            if (x > pre) return 0;
        }
        int la = list.get(n - k);
        if (la == 0) return list.get(n);
        else return list.get(n) / list.get(n - k);
        
    }

 

3、每个作业需要一天来完成,给个每个作业可以在[start, end]内任意一天内去做,每天只能做一个作业,求最多能做多少个作业。

 

sol:贪心的思想:先按照开始时间排序,那么第一天我先做哪个作业呢?肯定是要先做截止日期最近的任务。

    public int maxEvents(int[][] A) {
        int res = 0;
        Arrays.sort(A, Comparator.comparingInt(o -> o[0]));
        //维护任务的结束时间
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        //枚举任务的开始时间
        int i = 0;
        for (int d = 1; d <= 100000; d++) {
            //把第i天开始的任务的结束时间放到优先队列,优先队列按照任务的结束时间从小到大排序
            while (i < A.length && A[i][0] == d) pq.add(A[i++][1]);

            //抛掉已经无法完成的任务(这些任务的结束时间在d之前
            while (!pq.isEmpty() && pq.peek() < d) pq.poll();

            //完成截止日期最短的任务
            if (!pq.isEmpty()) {
                res++;
                pq.poll();
            }
            if (res == A.length) break;
        }
        return res;
    }

 

4、给一个target数组。还给了一个起始数组,初始化全部是1,每次可以把数组中的一个数替换为当前数组的和,问是否可以变成目标数组

 

 

sol:我们反过来考虑,假设最后达到了目标数组,那么变化的是最大的那个数,那么用最大的数减去其他数的和就是原数组,

举例说明:

比如1 3 5 变成了 3 5 9,那么根据 3 5 9我们知道最大值9是最后一步得到的,用9-8=1就是上次这个位置的数,也就是3 5 1

一直这样看最后能否得到一个全部是1的数组即可。

 

注意到数字的可能达到1e9,因此对于像1 100这样的用例,按照我们上面的做法,会变为1 99, 1 98 , 1 97 ... 1,1

这样,但是我们可以对此优化,发现一次处理得到的数依然比其他所有数的和还要打的话,要再做一次,这样的话,对于1 100这样的用例,我们可以一次减少1 1

具体看代码吧:

    public boolean isPossible(int[] target) {
        Arrays.sort(target);
        PriorityQueue<Integer> pq = new PriorityQueue<>((o1, o2) -> Integer.compare(o2, o1));
        int len = target.length;

        long s = 0;
        for (int i = 0; i < len; i++){
            s += target[i];
            pq.add(target[i]);
        }

        while (!pq.isEmpty()) {
            int maxNum = pq.poll();
            if (maxNum == 1) return true;
            //下一个数
            if (maxNum - (s - maxNum) < 0) return false;
            //能减多少次
            int cishu = (int) (maxNum / (s - maxNum));
            long nxt = maxNum - cishu * (s - maxNum);
            s = s - maxNum + nxt;
            pq.add((int) nxt);
        }
        //cannot reach here
        return true;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值