leetcode周赛 第 370 场周赛

2923. 找到冠军 I

一场比赛中共有 n 支队伍,按从 0 到  n - 1 编号。

给你一个下标从 0 开始、大小为 n * n 的二维布尔矩阵 grid 。对于满足 0 <= i, j <= n - 1 且 i != j 的所有 i, j :如果 grid[i][j] == 1,那么 i 队比 j 队  ;否则,j 队比 i 队  。

在这场比赛中,如果不存在某支强于 a 队的队伍,则认为 a 队将会是 冠军 。

返回这场比赛中将会成为冠军的队伍。

示例 1:

输入:grid = [[0,1],[0,0]]
输出:0
解释:比赛中有两支队伍。
grid[0][1] == 1 表示 0 队比 1 队强。所以 0 队是冠军。

示例 2:

输入:grid = [[0,0,1],[1,0,1],[0,0,0]]
输出:1
解释:比赛中有三支队伍。
grid[1][0] == 1 表示 1 队比 0 队强。
grid[1][2] == 1 表示 1 队比 2 队强。
所以 1 队是冠军。

提示:

  • n == grid.length
  • n == grid[i].length
  • 2 <= n <= 100
  • grid[i][j] 的值为 0 或 1
  • 对于满足 i != j 的所有 i, j ,grid[i][j] != grid[j][i] 均成立
  • 生成的输入满足:如果 a 队比 b 队强,b 队比 c 队强,那么 a 队比 c 队强

思路:

每个队伍都会比一次,一定会有一个答案,那么其实就很简单了,直接两层循环,直接判断哪个队伍胜场为n-1,哪个就是冠军。

class Solution {
    public int findChampion(int[][] grid) {
        int ans = 0;
        int n = grid.length;
        for (int i=0;i

2924. 找到冠军 II

一场比赛中共有 n 支队伍,按从 0 到  n - 1 编号。每支队伍也是 有向无环图(DAG) 上的一个节点。

给你一个整数 n 和一个下标从 0 开始、长度为 m 的二维整数数组 edges 表示这个有向无环图,其中 edges[i] = [ui, vi] 表示图中存在一条从 ui 队到 vi 队的有向边。

从 a 队到 b 队的有向边意味着 a 队比 b 队  ,也就是 b 队比 a 队  。

在这场比赛中,如果不存在某支强于 a 队的队伍,则认为 a 队将会是 冠军 。

如果这场比赛存在 唯一 一个冠军,则返回将会成为冠军的队伍。否则,返回 -1 。

注意

  •  是形如 a1, a2, ..., an, an+1 的一个序列,且满足:节点 a1 与节点 an+1 是同一个节点;节点 a1, a2, ..., an 互不相同;对于范围 [1, n] 中的每个 i ,均存在一条从节点 ai 到节点 ai+1 的有向边。
  • 有向无环图 是不存在任何环的有向图。

示例 1:

leetcode周赛 第 370 场周赛_第1张图片

输入:n = 3, edges = [[0,1],[1,2]]
输出:0
解释:1 队比 0 队弱。2 队比 1 队弱。所以冠军是 0 队。

示例 2:

leetcode周赛 第 370 场周赛_第2张图片

输入:n = 4, edges = [[0,2],[1,3],[1,2]]
输出:-1
解释:2 队比 0 队和 1 队弱。3 队比 1 队弱。但是 1 队和 0 队之间不存在强弱对比。所以答案是 -1 。

提示:

  • 1 <= n <= 100
  • m == edges.length
  • 0 <= m <= n * (n - 1) / 2
  • edges[i].length == 2
  • 0 <= edge[i][j] <= n - 1
  • edges[i][0] != edges[i][1]
  • 生成的输入满足:如果 a 队比 b 队强,就不存在 b 队比 a 队强
  • 生成的输入满足:如果 a 队比 b 队强,b 队比 c 队强,那么 a 队比 c 队强

思路:

因为冠军的队伍不止一个,并且是有向的,那其实就可以直接用入度出度的即可。只要没有败场的队伍,那就是冠军。说白了,其实就是入度为0的队伍,遍历一遍即可。

class Solution {
    public int findChampion(int n, int[][] edges) {
        int[] ru = new int[n];
        int m = edges.length;
        for (int i=0;i

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

有一棵 n 个节点的无向树,节点编号为 0 到 n - 1 ,根节点编号为 0 。给你一个长度为 n - 1 的二维整数数组 edges 表示这棵树,其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 有一条边。

同时给你一个长度为 n 下标从 0 开始的整数数组 values ,其中 values[i] 表示第 i 个节点的值。

一开始你的分数为 0 ,每次操作中,你将执行:

  • 选择节点 i 。
  • 将 values[i] 加入你的分数。
  • 将 values[i] 变为 0 。

如果从根节点出发,到任意叶子节点经过的路径上的节点值之和都不等于 0 ,那么我们称这棵树是 健康的 。

你可以对这棵树执行任意次操作,但要求执行完所有操作以后树是 健康的 ,请你返回你可以获得的 最大分数 。

示例 1:

leetcode周赛 第 370 场周赛_第3张图片

输入:edges = [[0,1],[0,2],[0,3],[2,4],[4,5]], values = [5,2,5,2,1,1]
输出:11
解释:我们可以选择节点 1 ,2 ,3 ,4 和 5 。根节点的值是非 0 的。所以从根出发到任意叶子节点路径上节点值之和都不为 0 。所以树是健康的。你的得分之和为 values[1] + values[2] + values[3] + values[4] + values[5] = 11 。
11 是你对树执行任意次操作以后可以获得的最大得分之和。

示例 2:

leetcode周赛 第 370 场周赛_第4张图片

输入:edges = [[0,1],[0,2],[1,3],[1,4],[2,5],[2,6]], values = [20,10,9,7,4,3,5]
输出:40
解释:我们选择节点 0 ,2 ,3 和 4 。
- 从 0 到 4 的节点值之和为 10 。
- 从 0 到 3 的节点值之和为 10 。
- 从 0 到 5 的节点值之和为 3 。
- 从 0 到 6 的节点值之和为 5 。
所以树是健康的。你的得分之和为 values[0] + values[2] + values[3] + values[4] = 40 。
40 是你对树执行任意次操作以后可以获得的最大得分之和。

提示:

  • 2 <= n <= 2 * 10^4
  • edges.length == n - 1
  • edges[i].length == 2
  • 0 <= ai, bi < n
  • values.length == n
  • 1 <= values[i] <= 10^9
  • 输入保证 edges 构成一棵合法的树。

思路:

一开始题目没看清楚……我以为是有向树,也没看到根节点是0…

这道题目用到的算法主要是用到了树形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 当作叶子。

我的代码又臭又长(给大家看看笑话就行)。。。

class Solution {
    // 将一些变量直接放入类变量
    private ArrayList> mp = new ArrayList>();
    private int[] v;
    public long maximumScoreAfterOperations(int[][] edges, int[] values) {
        v = values;
        mp.clear();
        int n = edges.length + 1;
        for(int i=0;i tmp = new ArrayList();
            mp.add(tmp);
        }
        for (int i=0;i tmp = mp.get(edges[i][0]);
            tmp.add(edges[i][1]);
            ArrayList t = mp.get(edges[i][1]);
            t.add(edges[i][0]);
        }
        long[] ans = dfs(0, -1);
        return ans[0];
    }
    // long数组第一个元素表示当前节点的最大价值和,第二个元素是当前节点的sum
    public long[] dfs(int root, int last) {
        ArrayList tmp = mp.get(root);
        int n = tmp.size();
        if (n == 1 && last != -1) {
            // 如果是叶子结点
            long[] t = new long[2];
            t[0] = 0;
            t[1] = v[root];
            return t;
        }
        long[][] child = new long[n][2];
        long sum = 0;
        long res = 0;
        for (int i=0;i= sum) {
            t[0] = cnt;
            t[1] = sum + v[root];
            
        } else {
            t[0] = sum;
            t[1] = sum + v[root];
        }
        return t;
    }
}

人家的代码:

class Solution {
    public long maximumScoreAfterOperations(int[][] edges, int[] values) {
        List[] g = new ArrayList[values.length];
        Arrays.setAll(g, e -> new ArrayList<>());
        g[0].add(-1); // 避免误把根节点当作叶子
        for (int[] e : edges) {
            int x = e[0], y = e[1];
            g[x].add(y);
            g[y].add(x);
        }

        // 先把所有分数加入答案
        long ans = 0;
        for (int v : values) {
            ans += v;
        }
        return ans - dfs(0, -1, g, values);
    }

    // dfs(x) 计算以 x 为根的子树是健康时,失去的最小分数
    private long dfs(int x, int fa, List[] g, int[] values) {
        if (g[x].size() == 1) { // x 是叶子
            return values[x];
        }
        long loss = 0; // 第二种情况
        for (int y : g[x]) {
            if (y != fa) {
                loss += dfs(y, x, g, values); // 计算以 y 为根的子树是健康时,失去的最小分数
            }
        }
        return Math.min(values[x], loss); // 两种情况取最小值
    }
}

2926. 平衡子序列的最大和

给你一个下标从 0 开始的整数数组 nums 。

nums 一个长度为 k 的 子序列 指的是选出 k 个 下标 i0 < i1 < ... < ik-1 ,如果这个子序列满足以下条件,我们说它是 平衡的 :

  • 对于范围 [1, k - 1] 内的所有 j ,nums[ij] - nums[ij-1] >= ij - ij-1 都成立。

nums 长度为 1 的 子序列 是平衡的。

请你返回一个整数,表示 nums 平衡 子序列里面的 最大元素和 。

一个数组的 子序列 指的是从原数组中删除一些元素(也可能一个元素也不删除)后,剩余元素保持相对顺序得到的 非空 新数组。

示例 1:

输入:nums = [3,3,5,6]
输出:14
解释:这个例子中,选择子序列 [3,5,6] ,下标为 0 ,2 和 3 的元素被选中。
nums[2] - nums[0] >= 2 - 0 。
nums[3] - nums[2] >= 3 - 2 。
所以,这是一个平衡子序列,且它的和是所有平衡子序列里最大的。
包含下标 1 ,2 和 3 的子序列也是一个平衡的子序列。
最大平衡子序列和为 14 。

示例 2:

输入:nums = [5,-1,-3,8]
输出:13
解释:这个例子中,选择子序列 [5,8] ,下标为 0 和 3 的元素被选中。
nums[3] - nums[0] >= 3 - 0 。
所以,这是一个平衡子序列,且它的和是所有平衡子序列里最大的。
最大平衡子序列和为 13 。

示例 3:

输入:nums = [-2,-1]
输出:-1
解释:这个例子中,选择子序列 [-1] 。
这是一个平衡子序列,而且它的和是 nums 所有平衡子序列里最大的。

提示:

  • 1 <= nums.length <= 10^5
  • -10^9 <= nums[i] <= 10^9

思路:

https://leetcode.cn/problems/maximum-balanced-subsequence-sum/

写不出来,直接给个链接先了。。。

class Solution {
    public long maxBalancedSubsequenceSum(int[] nums) {
        int n = nums.length;
        int[] b = new int[n];
        for (int i = 0; i < n; i++) {
            b[i] = nums[i] - i;
        }
        Arrays.sort(b);
 
        BIT t = new BIT(b.length + 1);
        for (int i = 0; i < n; i++) {
            // j 为 nums[i]-i 离散化后的值(从 1 开始)
            int j = Arrays.binarySearch(b, nums[i] - i) + 1;
            long f = Math.max(t.preMax(j), 0) + nums[i];
            t.update(j, f);
        }
        return t.preMax(b.length);
    }
}

// 树状数组模板(维护前缀最大值)
class BIT {
    private long[] tree;

    public BIT(int n) {
        tree = new long[n];
        Arrays.fill(tree, Long.MIN_VALUE);
    }

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

    public long preMax(int i) {
        long res = Long.MIN_VALUE;
        while (i > 0) {
            res = Math.max(res, tree[i]);
            i &= i - 1;
        }
        return res;
    }
}

你可能感兴趣的:(leetcode,算法)