Leetcode 第 128 场双周赛题解

Leetcode 第 128 场双周赛题解

题目1:3110. 字符串的分数

思路

模拟。

代码

/*
 * @lc app=leetcode.cn id=3110 lang=cpp
 *
 * [3110] 字符串的分数
 */

// @lc code=start
class Solution
{
public:
    int scoreOfString(string s)
    {
        int n = s.length();
        int sum = 0;
        for (int i = 0; i < n - 1; i++)
            sum += abs(s[i + 1] - s[i]);
        return sum;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(n),其中 n 是字符串 s 的长度。

空间复杂度:O(1)。

题目2:3111. 覆盖所有点的最少矩形数目

思路

降维:由于矩形的高没有限制,所以我们只需考虑 points 的横坐标。

算法:

  1. 将数组 points 按照横坐标从小到大的顺序排序。
  2. 为方便计算,假设第一个矩形左边还有一个矩形,初始化 right = −1,因为所有横坐标都是非负数。
  3. 遍历横坐标 x=points[i][0],如果 x > right,说明需要新建一个以 (x, 0) 为左下角的矩形,ans 加一。基于贪心的思想,矩形越大越能包含更多的点,更新右边界 right = point[0] + w。
  4. 最后,返回 ans。

代码

/*
 * @lc app=leetcode.cn id=3111 lang=cpp
 *
 * [3111] 覆盖所有点的最少矩形数目
 */

// @lc code=start

// 降维 + 排序 + 贪心

class Solution
{
public:
    int minRectanglesToCoverPoints(vector<vector<int>> &points, int w)
    {
        // 由于矩形的高没有限制,所以我们只需考虑 points 的横坐标。
        sort(points.begin(), points.end(),
             [&](const vector<int> &p1, const vector<int> &p2)
             { return p1[0] < p2[0]; });

        int ans = 0;
        int right = -1; // 矩形的右边界
        for (vector<int> &point : points)
        {
            if (point[0] > right) // 当前点的横坐标超过了矩形的右边界
            {
                ans++;
                // 以 x=point[0] 为左边界新建一个矩形,为了包含最多点,右边界为 point[0] + w
                right = point[0] + w;
            }
        }
        return ans;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(nlogn),其中 n 是数组 points 的长度。

空间复杂度:O(1)。

题目3:3112. 访问消失节点的最少时间

思路

对于本题,answer 几乎就是 dist 数组。只需要在 Dijkstra 算法的基础上,添加一处判断逻辑,在更新最短路之前,如果最短路长度 ≥disappear[i] 则不更新。

代码

/*
 * @lc app=leetcode.cn id=3112 lang=cpp
 *
 * [3112] 访问消失节点的最少时间
 */

// @lc code=start

// 堆优化 Dijkstra(适用于稀疏图)

class Solution
{
private:
    const int inf = INT_MAX / 2;

public:
    vector<int> minimumTime(int n, vector<vector<int>> &edges, vector<int> &disappear)
    {
        vector<vector<pair<int, int>>> g(n); // 邻接表
        for (auto &edge : edges)
        {
            int u = edge[0], v = edge[1], w = edge[2];
            g[u].emplace_back(v, w);
            g[v].emplace_back(u, w);
        }
        // dist[i] 表示点 k 到其他节点的最短距离
        vector<int> dist(n, inf);
        dist[0] = 0;
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
        pq.emplace(0, 0);
        while (!pq.empty())
        {
            auto [dx, x] = pq.top();
            pq.pop();
            if (dx > dist[x])
            { // x 之前出堆过
                continue;
            }
            for (auto &[y, d] : g[x])
            {
                int new_dist = dx + d;
                if (new_dist < disappear[y] && new_dist < dist[y])
                {
                    dist[y] = new_dist; // 更新 x 的邻居的最短路
                    pq.emplace(new_dist, y);
                }
            }
        }

        for (int &d : dist)
            if (d == inf)
                d = -1;
        return dist;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(n+mlogm),其中 m 是数组 edges 的长度。

空间复杂度:O(n+m),其中 m 是数组 edges 的长度。

题目4:3113. 边界元素是最大值的子数组数目

思路

例如 nums=[4,3,1,2,1],在从左到右遍历的过程中,由于 2 的出现,左边的 1 永远不可能与右边的 1 组成一个题目要求的子数组。所以当遍历到 2 时,左边的 1 就是无用数据了,可以清除。清除后我们会得到一个从左到右递减的数据结构:单调栈。

算法:

  1. 初始化答案等于 n,因为每个元素可以单独组成一个长为 1 的子数组,满足题目要求。
  2. 维护一个底大顶小的单调栈,记录元素及其出现次数。
  3. 从左到右遍历 nums,设当前元素为 x。只要 x 大于栈顶,就把栈顶出栈。如果 x 小于栈顶,把 x 及其出现次数 1 入栈。如果 x 等于栈顶,设栈顶记录的出现次数为 cnt,那么 x 可以和左边 cnt 个 x 组成 cnt 个满足要求的子数组,把答案增加 cnt,然后把 cnt 加一。
  4. 返回答案。

代码实现时,可以往栈底加入一个无穷大哨兵,从而简化判断逻辑。

代码

/*
 * @lc app=leetcode.cn id=3113 lang=cpp
 *
 * [3113] 边界元素是最大值的子数组数目
 */

// @lc code=start
class Solution
{
public:
    long long numberOfSubarrays(vector<int> &nums)
    {
        long long ans = 0LL;
        ans += nums.size(); // 每个单独的元素都是边界元素是最大值的子数组
        stack<pair<int, int>> stk;
        // 无穷大哨兵,不可能被弹出栈
        stk.push(make_pair(INT_MAX, 0));
        // 遍历
        for (int &x : nums)
        {
            // 比 x 小的元素都不可能构成边界
            while (x > stk.top().first)
                stk.pop();
            if (x == stk.top().first)
            {
                ans += stk.top().second;
                stk.top().second++;
            }
            else
                stk.push(make_pair(x, 1));
        }
        return ans;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(n),其中 n 是数组 nums 的长度。

空间复杂度:O(n),其中 n 是数组 nums 的长度。

  • 15
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值