LeetCode Weekly Contest 176

5340. 统计有序矩阵中的负数

给你一个 m * n 的矩阵 grid,矩阵中的元素无论是按行还是按列,都以非递增顺序排列。

请你统计并返回 grid 中 负数 的数目。

示例 1:

输入:grid = [[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]]
输出:8
解释:矩阵中共有 8 个负数。
示例 2:

输入:grid = [[3,2],[1,0]]
输出:0
示例 3:

输入:grid = [[1,-1],[-1,-1]]
输出:3
示例 4:

输入:grid = [[-1]]
输出:1

提示:

m == grid.length
n == grid[i].length
1 <= m, n <= 100
-100 <= grid[i][j] <= 100

思路

由于是比赛,直接暴力遍历,没有根据非递增性做优化

代码

class Solution {
    public int countNegatives(int[][] grid) {
        int cnt = 0;
        for (int[] row: grid) {
            for (int ele: row) {
                if (ele < 0) {
                    ++cnt;
                }
            }
        }
        return cnt;
    }
}

5341. 最后 K 个数的乘积

请你实现一个「数字乘积类」ProductOfNumbers,要求支持下述两种方法:

  1. add(int num):将数字 num 添加到当前数字列表的最后面。
  2. getProduct(int k):返回当前数字列表中,最后 k 个数字的乘积。

你可以假设当前列表中始终 至少 包含 k 个数字。
题目数据保证:任何时候,任一连续数字序列的乘积都在 32-bit 整数范围内,不会溢出。

示例:

输入:
[“ProductOfNumbers”,“add”,“add”,“add”,“add”,“add”,“getProduct”,“getProduct”,“getProduct”,“add”,“getProduct”]
[[],[3],[0],[2],[5],[4],[2],[3],[4],[8],[2]]

输出:
[null,null,null,null,null,null,20,40,0,null,32]

解释:

ProductOfNumbers productOfNumbers = new ProductOfNumbers();
productOfNumbers.add(3);        // [3]
productOfNumbers.add(0);        // [3,0]
productOfNumbers.add(2);        // [3,0,2]
productOfNumbers.add(5);        // [3,0,2,5]
productOfNumbers.add(4);        // [3,0,2,5,4]
productOfNumbers.getProduct(2); // 返回 20 。最后 2 个数字的乘积是 5 * 4 = 20
productOfNumbers.getProduct(3); // 返回 40 。最后 3 个数字的乘积是 2 * 5 * 4 = 40
productOfNumbers.getProduct(4); // 返回  0 。最后 4 个数字的乘积是 0 * 2 * 5 * 4 = 0
productOfNumbers.add(8);        // [3,0,2,5,4,8]
productOfNumbers.getProduct(2); // 返回 32 。最后 2 个数字的乘积是 4 * 8 = 32 

提示:

add 和 getProduct 两种操作加起来总共不会超过 40000 次。
0 <= num <= 100
1 <= k <= 40000

思路

非常简单的模拟题

代码

class ProductOfNumbers {
    private ArrayList<Integer> arr = new ArrayList<>();

    public ProductOfNumbers() {
        
    }
    
    public void add(int num) {
        arr.add(num);
    }
    
    public int getProduct(int k) {
        int prod = 1;
        for (int s=arr.size(), i=s-1; i>=s-k; --i) {
            prod *= arr.get(i);
        }
        return prod;
    }
}

/**
 * Your ProductOfNumbers object will be instantiated and called as such:
 * ProductOfNumbers obj = new ProductOfNumbers();
 * obj.add(num);
 * int param_2 = obj.getProduct(k);
 */

5342. 最多可以参加的会议数目

给你一个数组 events,其中 events[i] = [startDayi, endDayi] ,表示会议 i 开始于 startDayi ,结束于 endDayi 。

你可以在满足 startDayi <= d <= endDayi 中的任意一天 d 参加会议 i 。注意,一天只能参加一个会议。

请你返回你可以参加的 最大 会议数目。

示例 1:
在这里插入图片描述
输入:events = [[1,2],[2,3],[3,4]]
输出:3
解释:你可以参加所有的三个会议。
安排会议的一种方案如上图。
第 1 天参加第一个会议。
第 2 天参加第二个会议。
第 3 天参加第三个会议。
示例 2:

输入:events= [[1,2],[2,3],[3,4],[1,2]]
输出:4
示例 3:

输入:events = [[1,4],[4,4],[2,2],[3,4],[1,1]]
输出:4
示例 4:

输入:events = [[1,100000]]
输出:1
示例 5:

输入:events = [[1,1],[1,2],[1,3],[1,4],[1,5],[1,6],[1,7]]
输出:7

提示:

1 <= events.length <= 10^5
events[i].length == 2
1 <= events[i][0] <= events[i][1] <= 10^5

思路

贪心。将活动按主键为beginDay,副键为endDay升序排序,用一个优先队列(最小堆)维护剩下可选的活动的结束日期。遍历每一个日期,维护优先队列,做下面两件事情:

  1. 将当日开始的活动的结束日期访入优先队列
  2. 将当日已经结束的活动从优先队列中删除
  3. 从优先队列中取出一个最小的结束日期(此处体现『贪心』的思想)的活动参加,如果此时优先队列已经为空则跳过

每一个活动至多入队一次出队一次,因此时间复杂度为O(nlogn)

代码

class Solution {
    private class Event implements Comparable<Event> {
        public int begin, end;
        
        public Event(int begin, int end) {
            this.begin = begin;
            this.end = end;
        }
        
        @Override
        public int compareTo(Event other) {
            if (this.begin == other.begin) {
                return this.end - other.end;
            } else {
                return this.begin - other.begin;
            }
        }
    }
    
    public int maxEvents(int[][] events) {
        int n = events.length, i = 0, day = 1, cnt = 0, maxDay = 0;
        Event[] objs = new Event[n];
        for (int[] event: events) {
            if (event[1] > maxDay) {
                maxDay = event[1];
            }
            objs[i++] = new Event(event[0], event[1]);
        }
        Arrays.sort(objs);
        int minDay = objs[0].begin;
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        i = 0;
        for (day=minDay; day<=maxDay; ++day) {
            while (i < n && objs[i].begin == day) {
                pq.add(objs[i].end);
                ++i;
            }
            while (pq.size() > 0) {
                int curEnd = pq.poll();
                if (curEnd >= day) {
                    ++cnt;
                    break;
                }
            }
        }
        return cnt;
    }
}

5343. 多次求和构造目标数组

给你一个整数数组 target 。一开始,你有一个数组 A ,它的所有元素均为 1 ,你可以执行以下操作:

令 x 为你数组里所有元素的和
选择满足 0 <= i < target.size 的任意下标 i ,并让 A 数组里下标为 i 处的值为 x 。
你可以重复该过程任意次
如果能从 A 开始构造出目标数组 target ,请你返回 True ,否则返回 False 。

示例 1:

输入:target = [9,3,5]
输出:true
解释:从 [1, 1, 1] 开始
[1, 1, 1], 和为 3 ,选择下标 1
[1, 3, 1], 和为 5, 选择下标 2
[1, 3, 5], 和为 9, 选择下标 0
[9, 3, 5] 完成
示例 2:

输入:target = [1,1,1,2]
输出:false
解释:不可能从 [1,1,1,1] 出发构造目标数组。
示例 3:

输入:target = [8,5]
输出:true

提示:

N == target.length
1 <= target.length <= 5 * 10^4
1 <= target[i] <= 10^9

思路

个人认为这题没有第三题难,但是涉及int范围溢出的问题,容易出错。
思路也是用优先队列(这次是最大堆),每次取出优先队列首部的最大值,这个值必定是上次数列所有元素之和,因此将其减去当前数列中其他所有元素得到更新的最大值,恢复原数列,直到恢复成全为1的初始数列为止。如果在恢复过程中更新的最大值小于1,则说明不可恢复。
本题的注意点是数列所有元素和可能超出int范围,要用long表示

代码

class Solution {
    public boolean isPossible(int[] target) {
        PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        long sum = 0L;
        for (int tar: target) {
            sum += (long)tar;
            pq.add(tar);
        }
        long n = (long)target.length;
        while (true) {
            if (sum == n) {
                return true;
            }
            long curMax = (long)pq.poll(), preSum = sum;
            // System.out.println(curMax);
            sum -= sum - curMax;
            curMax -= preSum - curMax;
            // System.out.println(curMax);
            if (curMax <= 0L) {
                return false;
            }
            pq.add((int)curMax);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值