Leetcode 第 370 场周赛题解
- [Leetcode 第 370 场周赛题解](#Leetcode 第 370 场周赛题解)
Leetcode 第 370 场周赛题解
题目1:2923. 找到冠军 I
思路
模拟。
另解:如果第 j 列的元素值都是 0,说明没有队伍可以击败它,返回 j。
代码
cpp
/*
* @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(n^2^),其中 n 是二维数组 grid 的长度。
空间复杂度:O(n),其中 n 是二维数组 grid 的长度。
题目2:2924. 找到冠军 II
思路
模拟。
本质上是看是否恰好有 1 个入度为 0 的点。
若只有一个,返回这个点的下标;否则,返回 -1。
代码
cpp
/*
* @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 当作叶子。
代码
cpp
/*
* @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)
代码
cpp
/*
* @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 的长度。