Leetcode 第 370 场周赛题解

Leetcode 第 370 场周赛题解

  • Leetcode 第 370 场周赛题解
    • 题目1:2923. 找到冠军 I
      • 思路
      • 代码
      • 复杂度分析
    • 题目2:2924. 找到冠军 II
      • 思路
      • 代码
      • 复杂度分析
    • 题目3:2925. 在树上执行操作以后得到的最大分数
      • 思路
      • 代码
      • 复杂度分析
    • 题目4:2926. 平衡子序列的最大和
      • 思路
      • 代码
      • 复杂度分析

Leetcode 第 370 场周赛题解

题目1:2923. 找到冠军 I

思路

模拟。

另解:如果第 j 列的元素值都是 0,说明没有队伍可以击败它,返回 j。

代码

/*
 * @lc app=leetcode.cn id=2923 lang=cpp
 *
 * [2923] 找到冠军 I
 */

// @lc code=start
class Solution
{
public:
    int findChampion(vector<vector<int>> &grid)
    {
        int n = grid.size();
        vector<int> isChampion(n, true);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                if (grid[i][j])
                    isChampion[j] = false;
        for (int i = 0; i < n; i++)
            if (isChampion[i])
                return i;
        return -1;
    }
};
// @lc code=end

复杂度分析

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

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

题目2:2924. 找到冠军 II

思路

模拟。

本质上是看是否恰好有 1 个入度为 0 的点。

若只有一个,返回这个点的下标;否则,返回 -1。

代码

/*
 * @lc app=leetcode.cn id=2924 lang=cpp
 *
 * [2924] 找到冠军 II
 */

// @lc code=start
class Solution
{
public:
    int findChampion(int n, vector<vector<int>> &edges)
    {
        vector<int> isChampion(n, true);
        for (const vector<int> &edge : edges)
            isChampion[edge[1]] = false;
        int ans = -1;
        for (int i = 0; i < n; i++)
            if (isChampion[i])
            {
                if (ans != -1)
                    return -1;
                ans = i;
            }
        return ans;
    }
};
// @lc code=end

复杂度分析

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

空间复杂度:O(n)。

题目3:2925. 在树上执行操作以后得到的最大分数

思路

树形 DP。

正难则反,先把所有 values[i] 加到答案中,然后考虑哪些 values[i] 不能选(撤销,不加入答案)。

设当前节点为 x,计算以 x 为根的子树是健康时,失去的最小分数。那么答案就是 values 的元素和,减去「以 0 为根的子树是健康时,失去的最小分数」。

用「选或不选」分类讨论:

  • 第一种情况:失去 values[x],也就是不加入答案,那么 x 的所有子孙节点都可以加入答案,失去的最小分数就是 values[x]。
  • 第二种情况:values[x] 加入答案,问题变成「以 y 为根的子树是健康时,失去的最小分数」,这里 y 是 x 的儿子。如果有多个儿子,累加失去的最小分数。

这两种情况取最小值。注意第一种情况是不会往下递归的,所以当我们递归到叶子的时候,叶子一定不能加入答案,此时直接返回 values[x].

代码实现时,为了方便判断 x 是否为叶子节点,可以假设还有一条 0 到 −1 的边,这样不会误把根节点 0 当作叶子。

代码

/*
 * @lc app=leetcode.cn id=2925 lang=cpp
 *
 * [2925] 在树上执行操作以后得到的最大分数
 */

// @lc code=start
class Solution
{
public:
    long long maximumScoreAfterOperations(vector<vector<int>> &edges, vector<int> &values)
    {
        int n = values.size();
        vector<vector<int>> g(n);
        g[0].push_back(-1); // 避免误把根节点当作叶子
        for (vector<int> &edge : edges)
        {
            int x = edge[0], y = edge[1];
            g[x].push_back(y);
            g[y].push_back(x);
        }
        // dfs(x, fa) 计算以 x 为根的子树是健康时,失去的最小分数
        function<long long(int, int)> dfs = [&](int x, int father) -> long long
        {
            if (g[x].size() == 1) // x 是叶子
                return values[x];
            long long loss = 0;
            for (int &y : g[x])
            {
                if (y != father)
                {
                    // 计算以 y 为根的子树是健康时,失去的最小分数
                    loss += dfs(y, x);
                }
            }
            return min((long long)values[x], loss); // 两种情况取最小值
        };
        return accumulate(values.begin(), values.end(), 0LL) - dfs(0, -1);
    }
};
// @lc code=end

复杂度分析

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

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

题目4:2926. 平衡子序列的最大和

思路

题解:树状数组优化 DP(Python/Java/C++/Go)

代码

/*
 * @lc app=leetcode.cn id=2926 lang=cpp
 *
 * [2926] 平衡子序列的最大和
 */

// @lc code=start
// 树状数组模板(维护前缀最大值)
class BIT
{
    vector<long long> tree;

public:
    BIT(int n) : tree(n, LLONG_MIN) {}

    void update(int i, long long val)
    {
        while (i < tree.size())
        {
            tree[i] = max(tree[i], val);
            i += i & -i;
        }
    }

    long long pre_max(int i)
    {
        long long res = LLONG_MIN;
        while (i > 0)
        {
            res = max(res, tree[i]);
            i &= i - 1;
        }
        return res;
    }
};

class Solution
{
public:
    long long maxBalancedSubsequenceSum(vector<int> &nums)
    {
        int n = nums.size();
        // 离散化 nums[i]-i
        auto b = nums;
        for (int i = 0; i < n; i++)
        {
            b[i] -= i;
        }
        sort(b.begin(), b.end());
        b.erase(unique(b.begin(), b.end()), b.end()); // 去重

        BIT t = BIT(b.size() + 1);
        for (int i = 0; i < n; i++)
        {
            // j 为 nums[i]-i 离散化后的值(从 1 开始)
            int j = lower_bound(b.begin(), b.end(), nums[i] - i) - b.begin() + 1;
            long long f = max(t.pre_max(j), 0LL) + nums[i];
            t.update(j, f);
        }
        return t.pre_max(b.size());
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(nlogn),其中 n 为 nums 的长度。

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

你可能感兴趣的:(Every,day,a,leetcode,leetcode,算法,C++)