class Solution {
public int findChampion(int[][] grid) {
int n = grid.length;
for (int j = 0; ; j++) {
boolean ok = true;
for (int i = 0; i < n; i++) {
if (grid[i][j] != 0) { // 有队伍可以击败 j
ok = false;
break;
}
}
if (ok) {
return j;
}
}
}
}
一场比赛中共有 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:
输入:n = 3, edges = [[0,1],[1,2]] 输出:0 解释:1 队比 0 队弱。2 队比 1 队弱。所以冠军是 0 队。
示例 2:
输入:n = 4, edges = [[0,2],[1,3],[1,2]] 输出:-1 解释:2 队比 0 队和 1 队弱。3 队比 1 队弱。但是 1 队和 0 队之间不存在强弱对比。所以答案是 -1 。
class Solution {
public int findChampion(int n, int[][] edges) {
boolean[] isWeak = new boolean[n];
for (int[] e : edges) {
isWeak[e[1]] = true;
}
int ans = -1;
for (int i = 0; i < n; i++) {
if (isWeak[i]) {
continue;
}
if (ans != -1) {
return -1; // 冠军只能有一个
}
ans = i;
}
return ans;
}
}
有一棵 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:
输入: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:
输入: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 是你对树执行任意次操作以后可以获得的最大得分之和。
树形dp
class Solution {
public long maximumScoreAfterOperations(int[][] edges, int[] values) {
List<Integer>[] 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<Integer>[] 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); // 两种情况取最小值
}
}