LeetCode 第 217 场周赛 题解 C++

第 217 场周赛

来看完整版题解,给作者三连!!!

题目1:5613. 最富有客户的资产总量

思路:模拟
代码:
class Solution {
public:
    int maximumWealth(vector<vector<int>>& a) {
        int n = a.size();
        int m = a[0].size();
        int res = 0, pos = 0;
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                pos += a[i][j];
            }
            res = max(res, pos);
            pos = 0;
        }
        return res;
    }
};
复杂度分析:

时间复杂度为 O ( n m ) O(nm) O(nm),空间复杂度为 O ( 1 ) O(1) O(1)


## 题目2:5614. 找出最具竞争力的子序列
思路:栈

题目要求字典序最小的子序列,我们按照反向思维考虑,即如何从数组中删除 n − k n - k nk 个元素,保证剩下的元素组成的子序列字典序最小。

我们希望最终的子序列中,越靠前的元素越小,所以从前往后遍历,如果遇到某一个元素比之前的元素小,就将前面的元素删除,使得当前字典序较小的元素尽可能的处在子序列的左边部分。所以利用栈存储所有遍历过的元素,每次遍历到一个新元素时,不断弹出栈顶元素,直到栈顶元素小于当前元素,然后将当前元素压栈即可。

举个例子,对于给定数组 [ 2 , 4 , 3 , 3 , 5 , 4 , 9 , 6 ] [2, 4, 3, 3, 5, 4, 9, 6] [2,4,3,3,5,4,9,6],我们遍历元素, 2 , 4 2, 4 2,4 都被压栈,遍历到 3 3 3 时,发现栈顶元素 4 > 3 4 > 3 4>3,此时若将 4 4 4 弹出,最后构成的子序列头部为 2 , 3... 2, 3... 2,3... ,相较于不弹出时子序列头部的 2 , 4... 2, 4... 2,4... 字典序要小。故将 4 4 4 弹出, 3 3 3 入栈,继续遍历,后面的 3 , 5 3, 5 3,5 也依次压栈,遍历到 4 4 4 时,需要将 5 5 5 弹出,以此类推,最后得到的栈内元素为 [ 2 , 3 , 3 , 4 , 6 ] [2, 3, 3, 4, 6] [2,3,3,4,6],由于最后要保留 k = 4 k = 4 k=4 个元素,直接截取前四个即可。

特别地,为了保证最后能剩余 k k k 个元素,我们要记录弹出元素的个数。代码中维护变量 p o s pos pos 记录剩余待删除元素个数,每次弹出元素后进行 p o s pos pos 自减,若 $pos = 0 $ 则说明已经删除了需要删除的个数,剩余元素需要全部入栈。

代码:
class Solution {
public:
    vector<int> mostCompetitive(vector<int>& nums, int k) {
        stack<int> s;
        int n = nums.size();
        int pos = n - k; // 记录最多能删除的个数
        for(int x : nums) {
            if(pos > 0) {	// pos == 0 
                while(!s.empty() && s.top() > x) {
                    s.pop();
                    pos--;
                    if(pos == 0) break;
                }
            }
            s.push(x);
        }
        vector<int> ret;
        while(!s.empty()) {
            ret.push_back(s.top());
            s.pop();
        }
        reverse(ret.begin(), ret.end());
        ret.resize(k);
        return ret;
    }
};
复杂度分析:

时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n)


## 题目3:5615. 使数组互补的最少操作次数
思路:前缀和 + 差分

我们考虑任意一个数对 ( x , y ) (x, y) (x,y),不妨设 x < = y x <= y x<=y,那么将 y y y 调整为 1 1 1,二者和达到最小值 x + 1 x + 1 x+1 ,同理将 x x x 调整为 t a r g e t target target,二者和达到最大值 y + t a r g e t y + target y+target,所以对于一个数对 x + y x + y x+y,调整一个数后二者之和可以达到的范围为 [ x + 1 , y + t a r g e t ] [x + 1, y + target] [x+1,y+target],而这两数之外的范围,则需要调整两个数。我们假设任何一个数对初始状态都需要调整两次,利用差分数组:

x + 1 x + 1 x+1 处,操作次数减少一次; 在 y + t a r g e t + 1 y + target + 1 y+target+1 处,操作次数增加一次。

特别地,若二者之和为 x + y x + y x+y,则不需要进行调整,故:

x + y x + y x+y 处,操作次数再减少一次; 在 x + y + 1 x + y + 1 x+y+1 处,操作次数增加一次。

代码:
class Solution {
public:
    int minMoves(vector<int>& a, int limit) {
        int n = a.size();
        vector<int> v(limit * 2 + 2, 0);
        for (int i = 0; i < n / 2; i++) {
            int l = 1 + min(a[i], a[n - i - 1]);
            int r = limit + max(a[i], a[n - i - 1]);
            int sum = a[i] + a[n - i - 1];
            v[l]--, v[sum]--, v[sum + 1]++, v[r + 1]++;
        }
        int pos = n, res = n;
        for (int i = 2; i <= limit * 2; i++) {
            pos += v[i];
            res = min(res, pos);
        }
        return res;
    }
};

复杂度分析:

时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n)


题目4:5616. 数组的最小偏移量

思路:贪心

对于一个奇数,只能维持原值或乘以 2 2 2 变为偶数;而对于一个偶数,只能维持原值或不断除以 2 2 2 直到变成一个奇数。由于数值可能会双向变化,为了便于处理,我们将所有数值先变为其可变的最大值。然后不断取最大值的元素 m a x max max,若为奇数,则当前状态不需要再变化了,因为最大值已定,修改其余元素还可能减少最小值,导致差值变大;若为偶数,则将其变为 m a x / 2 max / 2 max/2,然后更新全局最大值和最小值,更新差值即可。为了动态获取最大值和最小值,可以利用优先队列。

代码:
class Solution {
public:
    int minimumDeviation(vector<int>& a) {
        int n = a.size();
        priority_queue<int> q;
        int mn = INT_MAX;
        int res = INT_MAX;
        for (int x : a) {
            if(x % 2 == 1) {
                q.push(x * 2);
                mn = min(mn, x * 2);
            } else {
                q.push(x);
                mn = min(mn, x);
            }
        }
        while (1) {
            int x = q.top();
            q.pop();
            res = min(res, x - mn); // 维护答案
            mn = min(mn, x / 2);    // 动态维护最小值
            if(x % 2 == 1) break;   // 若为奇数,就不需要再继续进行了
            q.push(x / 2);          // 除2后重新加入堆
        }
        return res;
    }
};

复杂度分析:

时间复杂度为 O ( n l g n ) O(nlgn) O(nlgn),空间复杂度为 O ( n ) O(n) O(n)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值