目录

LeetCode-440周赛

LeetCode — 440周赛

题目列表

一、将水果放入篮子 II

https://i-blog.csdnimg.cn/direct/7ef12696fd8043af9dbba88a8be5c15b.png

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

// 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
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 个元素

https://i-blog.csdnimg.cn/direct/6675d2007fab4ad29a5ce997823aa1e1.png

在所有满足

n u m s 1 [ j ] nums1[j]

n

u

m

s

1

[

j

] 小于

n u m s 1 [ i ] nums1[i]

n

u

m

s

1

[

i

] 的下标

j j

j 所对应的

n u m s 2 [ j ] nums2[j]

n

u

m

s

2

[

j

] 中选出最多

k k

k 个数,使得这些数字之和最大。

思路如下:

  • 如何快速找到小于

    n u m s 1 [ j ] nums1[j]

    n

    u

    m

    s

    1

    [

    j

    ] 小于

    n u m s 1 [ i ] nums1[i]

    n

    u

    m

    s

    1

    [

    i

    ] 的下标

    j j

    j 。我们可以对

    n u m s 1 nums1

    n

    u

    m

    s

    1 数组进行排序,那么在

    n u m s 1 [ i ] nums1[i]

    n

    u

    m

    s

    1

    [

    i

    ] 左边的数字都比它小

  • 如何求

    k k

    k 个数字的最大和,本质就是维护最大的

    k k

    k 个

    n u m s 2 [ j ] nums2[j]

    n

    u

    m

    s

    2

    [

    j

    ] ,这可以用最小堆来做:堆顶的元素是

    k k

    k 个数中的最小值,如果新加入的数字大于最小值,就加入堆中,同时丢弃最小值,否则则什么也不做,同时我们还要维护堆中元素之和

  • 注意:题目要求返回的

    a n s ans

    an

    s 中的

    a n s [ i ] ans[i]

    an

    s

    [

    i

    ] 要对应原始数组

    n u m s 1 nums1

    n

    u

    m

    s

    1 中的

    n u m s 1 [ i ] nums1[i]

    n

    u

    m

    s

    1

    [

    i

    ] ,所以我们不能对

    n u m s 1 nums1

    n

    u

    m

    s

    1 直接进行排序,我们只要通过下标对

    n u m s 1 nums1

    n

    u

    m

    s

    1 进行排序即可,具体看代码

代码如下

// 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
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

https://i-blog.csdnimg.cn/direct/301d67b36ad841558f0f92d85ebd811a.png

和第一题同样的题目,但是数据范围变大了,不能暴力模拟了,现在的关键是如何快速的找到

b a s k e t s baskets

ba

s

k

e

t

s 中最左边的第一个大于等于

f r u i t s [ i ] fruits[i]

f

r

u

i

t

s

[

i

] 的数字,如何做呢?思路如下:

  • 假设我们已知

    [ l , r ] [l,r]

    [

    l

    ,

    r

    ] 区间的最大值

    m x mx

    m

    x ,那么我们就能快速的判断出

    [ l , r ] [l,r]

    [

    l

    ,

    r

    ] 内是否有值

    ≥ f r u i t s [ i ] \ge fruits[i]

    f

    r

    u

    i

    t

    s

    [

    i

    ] ,从而决定是否进入区间去寻找

  • [ l , r ] [l,r]

    [

    l

    ,

    r

    ] 中,我们同样可以将区间分半成

    [ l , m i d ] [l,mid]

    [

    l

    ,

    mi

    d

    ] 和

    [ m i d + 1 , r ] [mid+1,r]

    [

    mi

    d

1

,

r

] ,然后用同样的方法先去判断

[ l , m i d ] [l,mid]

[

l

,

mi

d

] 中是否有值

≥ f r u i t s [ i ] \ge fruits[i]

f

r

u

i

t

s

[

i

] ,如果没有则去

[ m i d + 1 , r ] [mid+1,r]

[

mi

d

1

,

r

] 中找

  • 如此,问题的规模不断的被减半,直到

    l

    r l=r

    l

    =

    r 我们找到了一个

    b a s k e t s [ l ] ≥ f r u i t s [ i ] baskets[l]\ge fruits[i]

    ba

    s

    k

    e

    t

    s

    [

    l

    ]

    f

    r

    u

    i

    t

    s

    [

    i

    ] ,由于我们每次都是优先去左边查找,所以找到的

    b a s k e t s [ l ] baskets[l]

    ba

    s

    k

    e

    t

    s

    [

    l

    ] 就是

    b a s k e t s baskets

    ba

    s

    k

    e

    t

    s 中最左边的第一个大于等于

    f r u i t s [ i ] fruits[i]

    f

    r

    u

    i

    t

    s

    [

    i

    ] 的数。

  • 同时,由于问题规模不断减半,所以查询的时间复杂度为

    O ( l o g N ) O(logN)

    O

    (

    l

    o

    g

    N

    )

  • 综上所述,我们需要维护区间最大值,同时还要查询和进行单点修改,这些都可以用线段树来实现

具体代码如下

// 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
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

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

https://i-blog.csdnimg.cn/direct/97effd5396334137826d7e268bceac0b.png

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

  • 1、考虑不删除冲突,能得到的合法子数组的个数,记为

    b a s e base

    ba

    se

  • 2、考虑去掉一个冲突对后,会增加多少个合法子数组 记为

    e x t r a extra

    e

    x

    t

    r

    a

  • 答案为

    b a s e + m a x ( e x t r a ) base + max(extra)

    ba

    se

ma

x

(

e

x

t

r

a

)

如何求出

b a s e ? base?

ba

se

? 对于求解子数组的个数问题,我们一般通过枚举左端点

/ /

/ 右端点,来逐一统计合法子数组的个数,然后相加得到。这里我们从后往前枚举左端点

https://i-blog.csdnimg.cn/direct/8e2e6f935f0c45b2894c13f956a4792a.png

如何求出

e x t r a ? extra?

e

x

t

r

a

? 我们同样去模拟去掉一个冲突之后,哪些左端点的合法子数组增加了即可

https://i-blog.csdnimg.cn/direct/360cc8672d994eb78ad54c4617321d47.png

代码如下

// 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
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