昨晚还和老婆说,周赛一定会打的不错。果然今天就进前一百啦哈哈哈。
周赛传送门
5906. 句子中的有效单词数
思路:字符串分割,模拟
时间复杂度:
虽然是第一题,写起来还挺麻烦的。不过思路很简单,按空格分割,然后逐个单词检查即可。
class Solution {
public:
bool check(const std::string &s, int l, int r) {
if (l+1 >= r) {
return false;
}
int cnt0 = 0, cnt1 = 0;
for (int i = l+1; i < r; i++) {
if (!(('a' <= s[i] && s[i] <= 'z') || s[i] == '-' || s[i] == '!' || s[i] == ',' || s[i] == '.')){
return false;
}
if (s[i] == '-') {
cnt0++;
if (i == l+1 || i == r-1) {
return false;
}
if (!('a' <= s[i-1] && s[i-1] <= 'z')) {
return false;
}
if (!('a' <= s[i+1] && s[i+1] <= 'z')) {
return false;
}
}
if (s[i] == '!' || s[i] == ',' || s[i] == '.') {
cnt1++;
if (i != r-1) {
return false;
}
}
}
if (cnt0 > 1 || cnt1 > 1) {
return false;
}
return true;
}
int countValidWords(string s) {
s += " "; // 在最后添加一个空格,避免最后一个单词处理不到。
int anw = 0;
for (int i = 0, pre = -1; i < s.size(); i++) {
if (s[i] == ' ') {
if (check(s, pre, i)) {
anw ++;
}
pre = i;
}
}
return anw;
}
};
5907. 下一个更大的数值平衡数
思路:预处理,暴力枚举
时间复杂度:。 为数字长度。
不难发现,发现 1e6 的下一个数值平均数为 1224444。因此,可在 的时间复杂度下预处理出所有的 的答案。
int anw[1000001] = {0};
class Solution {
public:
Solution() {
if (anw[1000000] == 0) {
anw[1000000] = 1224444;
for (int i = 1000000; i >= 1; i--) {
if (check(i)) {
anw[i-1] = i;
} else {
anw[i-1] = anw[i];
}
}
}
}
bool check(int n) {
int cnt[10] = {0};
while(n > 0) {
cnt[n%10]++;
n /= 10;
}
for (int i = 0; i < 10; i++) {
if (cnt[i] != i && cnt[i] != 0) {
return false;
}
}
return true;
}
int nextBeautifulNumber(int n) {
return anw[n];
}
};
5908. 统计最高分的节点数目
思路:DFS统计子树的节点数
时间复杂度:
首先利用 DFS 在 的时间复杂度内统计出所有子树的节点数量。
当我们将节点 移除之后,整棵树最多会分为三部分:
- 节点 的左子树,记为
- 节点 的右子树,记为
- 其他节点组成的一棵树,记为
其中, 和 已经在 DFS 中得到,。则节点 的分数为
class Solution {
public:
int cnt[100000] = {0};
vector<int> edge[100000];
void dfs(int root) {
// cnt[i] 记录了以 i 为根的节点数量
cnt[root] = 1; // 先加上节点 i 自己
for (int i = 0; i < edge[root].size(); i++) {
int v = edge[root][i];
dfs(v);
cnt[root] += cnt[v]; // 累加上所有子树的
}
}
int countHighestScoreNodes(vector<int>& p) {
// 构造边表
for (int i = 0; i < p.size(); i++) {
if (p[i] != -1) {
edge[p[i]].emplace_back(i);
}
}
// dfs(i) 会得到以 i 根的子树的节点数量
dfs(0);
int64_t score = -1, anw = 0;
for (int i = 0; i < p.size(); i++) {
// 这里其实未区分左右子树,只判断有几棵子树。
int64_t pl = 0, pr = 0, po = 0;
if (edge[i].size() >= 1) {
pl = cnt[edge[i][0]];
}
if (edge[i].size() >= 2) {
pr = cnt[edge[i][1]];
}
po = p.size() - 1 - pl -pr;
int64_t s = max(1L, pl) * max(1L, pr) * max(1L, po);
if (s > score) {
score = s, anw = 1;
} else if (s == score) {
anw++;
}
}
return anw;
}
};
5909. 并行课程 III
思路:记忆化搜索求解最长路径
时间复杂度:
不难发现,本题是让求 DAG 上的最长路径,路径的长度即为路径上所有点的权值之和。
不妨先定义一个一维数组 , 表示以节点 为起点的最长路径的长度:
- 当节点 无出边时,。
- 当节点 有出边时,
最后,最大的 即为答案。
class Solution {
public:
// 边表
vector<int> edge[50001];
// cost[i] 表示从 i 出发到其后继节点中最远节点的距离
int cost[50001];
void dfs(int root, const vector<int> &time) {
// 如果 cost[root] != INT_MAX,说明一件计算过啦,无需再算。
if (cost[root] != INT_MAX) {
return;
}
int m = 0;
// 计算后继节点的 cost,并记录最大值至 m.
for (auto next : edge[root]) {
dfs(next, time);
if (m < cost[next]) {
m = cost[next];
}
}
// 因为该题中都是点权,所以 m 加上 root 节点的权值,即为 cost[root]
cost[root] = m + time[root-1];
}
int minimumTime(int n, vector<vector<int>>& r, vector<int>& time) {
// 构造边表
for (const auto &e : r) {
edge[e[0]].push_back(e[1]);
}
// 初始化
for (int i = 1; i <= time.size(); i++) {
cost[i] = INT_MAX;
}
// 计算所有的 cost[i]
for (int i = 1; i <= time.size(); i++) {
dfs(i, time);
}
// anw 为答案,即cost[i] 最大的那个。
int anw = 0;
for (int i = 1; i <= time.size(); i++) {
if (cost[i] > anw) {
anw = cost[i];
}
}
return anw;
}
};