LeetCode #12 双周赛
5097. 力扣排行榜
题目:设计一个排行榜,满足插入、前缀和、排序。
分析:数据范围很小,用不到 logn 的数据结构,直接暴力即可。
用
m
a
p
map
map 存编号对应成绩,每次查询暴力排序找前 k 和。
class Leaderboard {
public:
map<int, int> mp;
Leaderboard() {}
void addScore(int playerId, int score) {
mp[playerId] += score;
}
int top(int K) {
vector<int> arr;
for (auto it = mp.begin(); it != mp.end(); it++) {
arr.push_back(it->second);
}
sort(arr.begin(), arr.end());
int ans = 0, pos = arr.size() - 1;
while (K--) {
ans += arr[pos--];
}
return ans;
}
void reset(int playerId) {
mp[playerId] = 0;
}
};
5096. 数组变换
题目:
分析:按题意模拟
class Solution {
public:
vector<int> transformArray(vector<int>& arr) {
int n = arr.size(), flag = 1, p = 1;
vector<int> ans[2];
for (auto x : arr) {
ans[0].push_back(x); ans[1].push_back(x);
}
while (flag) {
flag = 0;
for (int i = 0; i < n; i++) {
if (i == 0 || i == n - 1) {
ans[p][i] = ans[p ^ 1][i];
} else if (ans[p ^ 1][i] > ans[p ^ 1][i - 1] && ans[p ^ 1][i] > ans[p ^ 1][i + 1]) {
ans[p][i] = ans[p ^ 1][i] - 1; flag = 1;
} else if (ans[p ^ 1][i] < ans[p ^ 1][i - 1] && ans[p ^ 1][i] < ans[p ^ 1][i + 1]) {
ans[p][i] = ans[p ^ 1][i] + 1; flag = 1;
} else {
ans[p][i] = ans[p ^ 1][i];
}
}
p ^= 1;
}
return ans[p ^ 1];
}
};
5098. 树的直径
题目:求树的直径(树上最长路径)
分析:经典题目,先建树,
用
d
p
[
i
]
dp[i]
dp[i] 维护以 i 为根的子树的深度,
d
p
[
i
]
=
m
a
x
(
d
p
[
i
]
,
d
p
[
j
]
+
1
)
dp[i] = max(dp[i], dp[j]+1)
dp[i]=max(dp[i],dp[j]+1)(j 为 i 的儿子)。
求出任意节点 i 子树深度之后,经过 i 的最长路径就是最深的两颗子树深度相加。
经过点 i i i 的最长路径就是最深的两颗子树深度相加。最后答案就是所有点的最大值。
class Solution {
public:
void DP(int* dp, int rt, vector<int>* son, int& ans) {
dp[rt] = 0;
int m1 = 0, m2 = 0;
for (auto x : son[rt]) {
DP(dp, x, son, ans);
if(dp[x] + 1 > m1){
m2 = m1;
m1 = dp[x] + 1;
}else if(dp[x] + 1 > m2){
m2 = dp[x] + 1;
}
}
dp[rt] = m1;
ans = max(ans, m1 + m2);
}
int treeDiameter(vector<vector<int>>& edges) {
vector<int> son[10010];
int dp[10010];
int n = 0, vis[10010], rt, ans = 0;
for (auto x : edges) {
son[x[0]].push_back(x[1]);
n = max(n, max(x[0], x[1]));
vis[x[1]]++;
}
for (int i = 0; i <= n; i++) {
if (!vis[i]) {
rt = i; break;
}
}
DP(dp, rt, son, ans);
return ans;
}
};
5115. 删除回文子数组
题目:给一个数组,每次可以删去回文子数组,删去之后右边的右移。问删去整个数组所需要的最小操作次数。
分析:明显区间 dp,用 d p [ s ] [ e ] dp[s][e] dp[s][e] 表示删去 ( s , e ) (s, e) (s,e) 所需要最小操作数,最终答案就是 d p [ 0 ] [ n − 1 ] dp[0][n-1] dp[0][n−1]。
转移方程:
d
p
[
s
]
[
e
]
=
m
i
n
(
d
p
[
s
]
[
e
]
,
d
p
[
s
]
[
k
]
+
d
p
[
k
+
1
]
[
e
]
dp[s][e] = min(dp[s][e], dp[s][k] + dp[k+1][e]
dp[s][e]=min(dp[s][e],dp[s][k]+dp[k+1][e]
当区间两端点相同时,有:
d
p
[
s
]
[
e
]
=
m
i
n
(
d
p
[
s
]
[
e
]
,
d
p
[
s
+
1
]
[
e
−
1
]
dp[s][e] = min(dp[s][e], dp[s+1][e-1]
dp[s][e]=min(dp[s][e],dp[s+1][e−1],即两端点可以跟里面区间最后一次一起删。
class Solution {
const int N = 1e2 + 10;
const int INF = 0x3f3f3f;
public:
int minimumMoves(vector<int>& v) {
int n = v.size();
int dp[N][N];
memset(dp, INF, sizeof(dp));
for (int i = 0; i < n; i++) {
dp[i][i] = 1;
}
for (int l = 2; l <= n; l++) { // 枚举区间长度, 长度为 1 的已经初始化了
for (int s = 0; s + l - 1 < n; s++) { // 枚举区间起点 s
int e = s + l - 1;
for (int k = s; k <= e; k++) { // 找中点位置
dp[s][e] = min(dp[s][e], dp[s][k] + dp[k + 1][e]);
}
if (v[s] == v[e]) {
if (l == 2) dp[s][e] = 1;
else dp[s][e] = min(dp[s][e], dp[s + 1][e - 1]);
}
}
}
return dp[0][n - 1];
}
};
本文深入解析LeetCode双周赛的三道难题:力扣排行榜、数组变换及树的直径,提供详细的算法思路与代码实现,涵盖数据结构、区间动态规划等核心概念。
1184

被折叠的 条评论
为什么被折叠?



