Leetcode 第 409 场周赛题解

Leetcode 第 409 场周赛题解

题目1:3242. 设计相邻元素求和服务

思路

模拟,在原数组的周围补一圈 0。

代码

/*
 * @lc app=leetcode.cn id=3242 lang=cpp
 *
 * [3242] 设计相邻元素求和服务
 */

// @lc code=start
class NeighborSum
{
private:
    int g[12][12];
    int n;

public:
    NeighborSum(vector<vector<int>> &grid) : n(grid.size())
    {
        for (int i = 0; i <= n + 1; i++)
            for (int j = 0; j <= n + 1; j++)
                g[i][j] = 0;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                g[i][j] = grid[i - 1][j - 1];
    }

    int adjacentSum(int value)
    {
        int ans = 0;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                if (g[i][j] == value)
                    ans = g[i][j - 1] + g[i + 1][j] + g[i - 1][j] + g[i][j + 1];

        return ans;
    }

    int diagonalSum(int value)
    {
        int ans = 0;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                if (g[i][j] == value)
                    ans = g[i - 1][j - 1] + g[i - 1][j + 1] + g[i + 1][j - 1] + g[i + 1][j + 1];
        return ans;
    }
};

/**
 * Your NeighborSum object will be instantiated and called as such:
 * NeighborSum* obj = new NeighborSum(grid);
 * int param_1 = obj->adjacentSum(value);
 * int param_2 = obj->diagonalSum(value);
 */
// @lc code=end

复杂度分析

时间复杂度:O(n2),其中 n 是数组 grid 的行(列)数。

空间复杂度:O(n2),其中 n 是数组 grid 的行(列)数。

题目2:3243. 新增道路查询后的最短距离 I

思路

动态规划。

定义 dp[i] 为从 0 到 i 的最短路。

用 from[i] 记录额外添加的边的终点是 i,起点列表是 from[i]。

我们可以从 i−1 到 i,也可以从 from[i][j] 到 i,这些位置作为转移来源,用其 dp 值 +1 更新 dp[i] 的最小值。

初始值:dp[i]=i。

答案:dp[n−1]。

注意:设添加的边为 l→r,只有当 dp[l]+1<dp[r] 时才更新 DP。

代码

/*
 * @lc app=leetcode.cn id=3243 lang=cpp
 *
 * [3243] 新增道路查询后的最短距离 I
 */

// @lc code=start

// 动态规划

class Solution
{
public:
    vector<int> shortestDistanceAfterQueries(int n, vector<vector<int>> &queries)
    {
        vector<int> dp(n);
        vector<vector<int>> from(n);
        // 初始化:dp[i] = i
        iota(dp.begin(), dp.end(), 0);

        vector<int> ans;
        // 状态转移
        for (vector<int> &q : queries)
        {
            int l = q[0], r = q[1];
            from[r].push_back(l);
            if (dp[l] + 1 < dp[r])
            {
                dp[r] = dp[l] + 1;
                // 更新后面的最短路
                for (int i = r + 1; i < n; i++)
                {
                    dp[i] = min(dp[i], dp[i - 1] + 1);
                    for (int j : from[i])
                    {
                        // 存在一条 j->i 的路径
                        dp[i] = min(dp[i], dp[j] + 1);
                    }
                }
            }
            ans.push_back(dp[n-1]);
        }
        return ans;
    }
};
// @lc code=end

复杂度分析

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

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

题目3:3244. 新增道路查询后的最短距离 II

思路

贪心。

由于题目保证添加的边(捷径)不会交叉,从贪心的角度看,遇到捷径就走捷径是最优的。所有被跳过的城市都不可能再出现在最短路了,直接删除掉。

代码

/*
 * @lc app=leetcode.cn id=3244 lang=cpp
 *
 * [3244] 新增道路查询后的最短距离 II
 */

// @lc code=start
class Solution
{
public:
    vector<int> shortestDistanceAfterQueries(int n, vector<vector<int>> &queries)
    {
        set<int> s; // 存储所有城市的编号
        // 将每个城市加入到 set 中
        for (int i = 0; i < n; i++)
            s.insert(i);

        vector<int> ans;
        for (vector<int> &q : queries)
        {
            // [l, r) 之间的所有城市不可能在最短路里
            auto l = s.upper_bound(q[0]), r = s.lower_bound(q[1]);
            s.erase(l, r);
            // 剩余节点数减 1 即为当前的最短路径长度
            ans.push_back(s.size() - 1);
        }
        return ans;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O((n+q)logn),其中 q 是数组 queries 的长度。

空间复杂度:O(n)。

题目4:3245. 交替组 III

思路

树状数组。

题解:https://leetcode.cn/problems/alternating-groups-iii/solutions/2869147/cong-yi-dao-nan-yi-bu-bu-tui-dao-pythonj-jho9/

代码

/*
 * @lc app=leetcode.cn id=3245 lang=cpp
 *
 * [3245] 交替组 III
 */

// @lc code=start
class FenwickTree
{
    vector<array<int, 2>> tree;

public:
    FenwickTree(int n) : tree(n + 1) {}

    // op=1,添加一个 size
    // op=-1,移除一个 size
    void update(int size, int op)
    {
        for (int i = tree.size() - size; i < tree.size(); i += i & -i)
        {
            tree[i][0] += op;
            tree[i][1] += op * size;
        }
    }

    // 返回 >= size 的元素个数,元素和
    pair<int, int> query(int size)
    {
        int cnt = 0, sum = 0;
        for (int i = tree.size() - size; i > 0; i &= i - 1)
        {
            cnt += tree[i][0];
            sum += tree[i][1];
        }
        return {cnt, sum};
    }
};

class Solution
{
public:
    vector<int> numberOfAlternatingGroups(vector<int> &a, vector<vector<int>> &queries)
    {
        int n = a.size();
        set<int> s;
        FenwickTree t(n);

        // op=1,添加一个结束位置 i
        // op=-1,移除一个结束位置 i
        auto update = [&](int i, int op)
        {
            auto it = s.lower_bound(i);
            int pre = it == s.begin() ? *s.rbegin() : *prev(it);
            int nxt = it == s.end() ? *s.begin() : *it;

            t.update((nxt - pre + n - 1) % n + 1, -op); // 移除/添加旧长度
            t.update((i - pre + n) % n, op);
            t.update((nxt - i + n) % n, op); // 添加/移除新长度
        };

        // 添加一个结束位置 i
        auto add = [&](int i)
        {
            if (s.empty())
            {
                t.update(n, 1);
            }
            else
            {
                update(i, 1);
            }
            s.insert(i);
        };

        // 移除一个结束位置 i
        auto del = [&](int i)
        {
            s.erase(i);
            if (s.empty())
            {
                t.update(n, -1);
            }
            else
            {
                update(i, -1);
            }
        };

        for (int i = 0; i < n; i++)
        {
            if (a[i] == a[(i + 1) % n])
            {
                add(i); // i 是一个结束位置
            }
        }

        vector<int> ans;
        for (auto &q : queries)
        {
            if (q[0] == 1)
            {
                if (s.empty())
                {
                    ans.push_back(n); // 每个长为 size 的子数组都符合要求
                }
                else
                {
                    auto [cnt, sum] = t.query(q[1]);
                    ans.push_back(sum - cnt * (q[1] - 1));
                }
            }
            else
            {
                int i = q[1];
                if (a[i] == q[2])
                { // 无影响
                    continue;
                }
                int pre = (i - 1 + n) % n, nxt = (i + 1) % n;
                // 修改前,先去掉结束位置
                if (a[pre] == a[i])
                {
                    del(pre);
                }
                if (a[i] == a[nxt])
                {
                    del(i);
                }
                a[i] ^= 1;
                // 修改后,添加新的结束位置
                if (a[pre] == a[i])
                {
                    add(pre);
                }
                if (a[i] == a[nxt])
                {
                    add(i);
                }
            }
        }
        return ans;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O((n+q)logn),其中 n 是数组 colors 的长度,q 是数组 queries 的长度。

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

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

UestcXiye

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

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

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

打赏作者

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

抵扣说明:

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

余额充值