LeetCode --- 440周赛

题目列表

3477. 将水果放入篮子 II
3478. 选出和最大的 K 个元素
3479. 将水果装入篮子 III
3480. 删除一个冲突对后最大子数组数目

一、将水果放入篮子 II

本题由于数据范围比较小,所以我们可以暴力模拟水果的放置过程,代码如下

cpp 复制代码
// C++
class Solution {
public:
    int numOfUnplacedFruits(vector<int>& fruits, vector<int>& baskets) {
        int n = fruits.size();
        int cnt = n;
        for(int x : fruits){
            for(int& y : baskets){
                if(y > 0 && x <= y){
                    cnt--;
                    y = -1;
                    break;
                }
            }
        }
        return cnt;
    }
};
python 复制代码
# Python
class Solution:
    def numOfUnplacedFruits(self, fruits: List[int], baskets: List[int]) -> int:
        cnt = len(fruits)
        for x in fruits:
            for i, y in enumerate(baskets):
                if y > 0 and x <= y:
                    baskets[i] = -1
                    cnt -= 1
                    break
        return cnt

二、选出和最大的 K 个元素

在所有满足 n u m s 1 j nums1j nums1j 小于 n u m s 1 i nums1i nums1i 的下标 j j j 所对应的 n u m s 2 j nums2j nums2j 中选出最多 k k k 个数,使得这些数字之和最大。

思路如下:

  • 如何快速找到小于 n u m s 1 j nums1j nums1j 小于 n u m s 1 i nums1i nums1i 的下标 j j j。我们可以对 n u m s 1 nums1 nums1 数组进行排序,那么在 n u m s 1 i nums1i nums1i 左边的数字都比它小
  • 如何求 k k k 个数字的最大和,本质就是维护最大的 k k k 个 n u m s 2 j nums2j nums2j,这可以用最小堆来做:堆顶的元素是 k k k 个数中的最小值,如果新加入的数字大于最小值,就加入堆中,同时丢弃最小值,否则则什么也不做,同时我们还要维护堆中元素之和
  • 注意:题目要求返回的 a n s ans ans 中的 a n s i ansi ansi 要对应原始数组 n u m s 1 nums1 nums1 中的 n u m s 1 i nums1i nums1i,所以我们不能对 n u m s 1 nums1 nums1 直接进行排序,我们只要通过下标对 n u m s 1 nums1 nums1 进行排序即可,具体看代码

代码如下

cpp 复制代码
// C++
class Solution {
public:
    vector<long long> findMaxSum(vector<int>& nums1, vector<int>& nums2, int k) {
        int n = nums1.size();
        vector<int> idx(n);
        iota(idx.begin(), idx.end(), 0); // 给 idx 数组赋值 0,1,2,...
        ranges::sort(idx, {}, [&](auto x){ return nums1[x]; }); // 对下标进行排序
        long long sum = 0;
        vector<long long> ans(n);
        priority_queue<int, vector<int>, greater<>> pq; // 最小堆
        for(int i = 0, j = 0; i < n; i++){
            int x = idx[i]; // 注意这里的 i,j 需要通过 idx 进行映射到真正的下标
            while(j < i && nums1[idx[j]] < nums1[x]){ // 比 nums1[x] 小的考虑入堆 
                pq.push(nums2[idx[j]]);
                sum += nums2[idx[j]];
                if(pq.size() > k){
                    sum -= pq.top();
                    pq.pop();
                }
                j++;
            }
            ans[x] = sum;
        }
        return ans;
    }
};
python 复制代码
# Python
class Solution:
    def findMaxSum(self, nums1: List[int], nums2: List[int], k: int) -> List[int]:
        n = len(nums1)
        idx = [0] * n
        for i in range(n):
            idx[i] = i
        idx.sort(key=lambda x : nums1[x])
        j = sum = 0
        pq = []
        ans = [0] * n
        for i in range(n):
            x = idx[i]
            while j < i and nums1[idx[j]] < nums1[x]:
                heappush(pq, nums2[idx[j]])
                sum += nums2[idx[j]]
                if len(pq) > k:
                    sum -= heappop(pq)
                j += 1
            ans[x] = sum
        return ans

三、将水果放入篮子 III

和第一题同样的题目,但是数据范围变大了,不能暴力模拟了,现在的关键是如何快速的找到 b a s k e t s baskets baskets 中最左边的第一个大于等于 f r u i t s i fruitsi fruitsi 的数字,如何做呢?思路如下:

  • 假设我们已知 l , r l,r l,r 区间的最大值 m x mx mx,那么我们就能快速的判断出 l , r l,r l,r 内是否有值 ≥ f r u i t s i \ge fruitsi ≥fruitsi,从而决定是否进入区间去寻找
  • l , r l,r l,r 中,我们同样可以将区间分半成 l , m i d l,mid l,mid m i d + 1 , r mid+1,r mid+1,r,然后用同样的方法先去判断 l , m i d l,mid l,mid 中是否有值 ≥ f r u i t s i \ge fruitsi ≥fruitsi,如果没有则去 m i d + 1 , r mid+1,r mid+1,r 中找
  • 如此,问题的规模不断的被减半,直到 l = r l=r l=r 我们找到了一个 b a s k e t s l ≥ f r u i t s i basketsl\ge fruitsi basketsl≥fruitsi,由于我们每次都是优先去左边查找,所以找到的 b a s k e t s l basketsl basketsl 就是 b a s k e t s baskets baskets 中最左边的第一个大于等于 f r u i t s i fruitsi fruitsi 的数。
  • 同时,由于问题规模不断减半,所以查询的时间复杂度为 O ( l o g N ) O(logN) O(logN)
  • 综上所述,我们需要维护区间最大值,同时还要查询和进行单点修改,这些都可以用线段树来实现

具体代码如下

cpp 复制代码
// C++
class SegTree{
public:
    SegTree(int n) : t(n << 2) {}
    void maintain(int o){
        t[o] = max(t[o << 1], t[o << 1|1]);
    }
    void build(int o, int l, int r, const vector<int>& a){
        if(l == r){
            t[o] = a[l];
            return;
        }
        int m = l + (r - l) / 2;
        build(o << 1, l, m, a);
        build(o << 1|1, m + 1, r, a);
        maintain(o);
    }
    void update(int o, int l, int r, int i, int val = -1){
        if(l == r){
            t[o] = val;
            return;
        }
        int m = l + (r - l) / 2;
        if(i <= m) update(o << 1, l, m, i, val);
        else update(o << 1 | 1, m + 1, r, i, val);
        maintain(o);
    }
    int query(int o, int l, int r, int val){
        if(t[o] < val){
            return -1;
        }
        if(l == r){
            return l;
        }
        int m = l + (r - l) / 2;
        int i = query(o << 1, l, m, val);
        if(i < 0)
            i = query(o << 1 | 1, m + 1, r, val);
        return i;
    }
private:
    vector<int> t; // 记录最大值
};
class Solution {
public:
    int numOfUnplacedFruits(vector<int>& fruits, vector<int>& baskets) {
        int n = fruits.size(), cnt = n;
        SegTree t(n);
        t.build(1, 0, n - 1, baskets);
        for(int x : fruits){
            int i = t.query(1, 0, n - 1, x);
            if(i >= 0){
                t.update(1, 0, n - 1, i);
                cnt--;
            }
        }
        return cnt;
    }
};
python 复制代码
# Python
class SegTree:
    def __init__(self, a:List[int]):
        n = len(a)
        self.t = [0] * (n << 2)
        self.build(1, 0, n - 1, a)

    def maintain(self, o:int):
        self.t[o] = max(self.t[o<<1], self.t[o<<1|1])

    def build(self, o:int, l:int, r:int, a:List[int]):
        if l == r:
            self.t[o] = a[l]
            return
        m = (l + r) // 2
        self.build(o << 1, l, m, a)
        self.build(o << 1|1, m + 1, r, a)
        self.maintain(o)

    def update(self, o:int, l:int, r:int, i:int, val:int):
        if l == r:
            self.t[o] = val
            return
        m = (l + r) // 2
        if i <= m:
            self.update(o << 1, l, m, i, val)
        else:
            self.update(o << 1|1, m + 1, r, i, val)
        self.maintain(o)

    def query(self, o:int, l:int, r:int, val:int)->int:
        if self.t[o] < val:
            return -1
        if l == r:
            return l
        m = (l + r) // 2
        i = self.query(o << 1, l, m, val)
        if i < 0:
            i = self.query(o << 1|1, m + 1, r, val)
        return i

class Solution:
    def numOfUnplacedFruits(self, fruits: List[int], baskets: List[int]) -> int:
        n = cnt = len(fruits)
        t = SegTree(baskets)
        for x in fruits:
            i = t.query(1, 0, n - 1, x)
            if i >= 0:
                t.update(1, 0, n - 1, i, -1)
                cnt -= 1
        return cnt

四、删除一个冲突对后最大子数组数目

题目问删除一个冲突后,能得到的合法子数组的最大个数。大致思路如下

  • 1、考虑不删除冲突,能得到的合法子数组的个数,记为 b a s e base base
  • 2、考虑去掉一个冲突对后,会增加多少个合法子数组 记为 e x t r a extra extra
  • 答案为 b a s e + m a x ( e x t r a ) base + max(extra) base+max(extra)

如何求出 b a s e ? base? base? 对于求解子数组的个数问题,我们一般通过枚举左端点 / / / 右端点,来逐一统计合法子数组的个数,然后相加得到。这里我们从后往前枚举左端点

如何求出 e x t r a ? extra? extra? 我们同样去模拟去掉一个冲突之后,哪些左端点的合法子数组增加了即可

代码如下

cpp 复制代码
// C++
class Solution {
public:
    long long maxSubarrays(int n, vector<vector<int>>& conflictingPairs) {
        long long ans = 0;
        // 只需要知道前最小值,次小值即可
        vector<array<int, 2>> g(n + 1, {n + 1, n + 1});
        for(auto& conflict: conflictingPairs){
            int x = conflict[0], y = conflict[1];
            if(x > y) swap(x, y);
            if(y < g[x][0]) g[x][1] = g[x][0], g[x][0] = y;
            else if(y < g[x][1]) g[x][1] = y;
        }
        vector<long long> extra(n + 2);
        int b0 = n + 1, b1 = n + 1;
        for(int i = n; i > 0; i--){
            auto& v = g[i];
            if(v[0] < b0) b1 = b0, b0 = v[0];
            else if(v[0] < b1) b1 = v[0];
            if(v[1] < b0) b1 = b0, b0 = v[1];
            else if(v[1] < b1) b1 = v[1];
            ans += b0 - i;
            extra[b0] += b1 - b0;
        }
        return ans + ranges::max(extra);
    }
};

// 优化
class Solution {
public:
    long long maxSubarrays(int n, vector<vector<int>>& conflictingPairs) {
        long long ans = 0;
        // 只需要知道前最小值,次小值即可
        vector<array<int, 2>> g(n + 1, {n + 1, n + 1});
        for(auto& conflict: conflictingPairs){
            int x = conflict[0], y = conflict[1];
            if(x > y) swap(x, y);
            if(y < g[x][0]) g[x][1] = g[x][0], g[x][0] = y;
            else if(y < g[x][1]) g[x][1] = y;
        }
        // vector<long long> extra(n + 2);
        // 由于 b0 是单调递减的,所以可以用 pre_b0, mx 来维护 extra 的最大值
        int b0 = n + 1, b1 = n + 1, pre_b0 = n + 1;
        long long s = 0, mx = 0;
        for(int i = n; i > 0; i--){
            auto& v = g[i];
            if(v[0] < b0) b1 = b0, b0 = v[0];
            else if(v[0] < b1) b1 = v[0];
            if(v[1] < b0) b1 = b0, b0 = v[1];
            else if(v[1] < b1) b1 = v[1];
            ans += b0 - i;
            if(pre_b0 != b0){
                pre_b0 = b0;
                s = 0;
            }
            s += b1 - b0;
            mx = max(mx, s);
        }
        return ans + mx;
    }
};
python 复制代码
# Python
class Solution:
    def maxSubarrays(self, n: int, conflictingPairs: List[List[int]]) -> int:
        g = [[n+1, n+1] for _ in range(n + 1)]
        for conflict in conflictingPairs:
            x, y = conflict[0], conflict[1]
            if x > y:
                x, y = y, x
            if y < g[x][0]:
                g[x][1], g[x][0] = g[x][0], y
            elif y < g[x][1]:
                g[x][1] = y
        base = mx = s = 0
        b0 = b1 = pre_b0 = n + 1
        for i in range(n, 0, -1):
            if g[i][0] < b0:
                b0, b1 = g[i][0], b0
            elif g[i][0] < b1:
                b1 = g[i][0]
            if g[i][1] < b0:
                b0, b1 = g[i][1], b0
            elif g[i][1] < b1:
                b1 = g[i][1]
            if b0 != pre_b0:
                pre_b0 = b0
                s = 0
            base += b0 - i
            s += b1 - b0
            mx = max(mx, s)
        return base + mx
相关推荐
江畔柳前堤20 小时前
大语言模型分布式训练:从并行策略到万卡工程的系统梳理
人工智能·分布式·深度学习·算法·目标检测·机器学习·语言模型
Forever Nore21 小时前
LeetCode 4 寻找两个正序数组的中位数 - 二分
算法·leetcode
罗西的思考1 天前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX1 天前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面1 天前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号1 天前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽1 天前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI1 天前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
地平线开发者1 天前
征程6工具链模型X86推理方式说明
算法
地平线开发者1 天前
【征程6】校准量化中HistogramObserver解析
算法