LeetCode第45场双周赛-解题报告
A. 唯一元素的和
原题链接
https://leetcode-cn.com/problems/sum-of-unique-elements/
解题思路
因为数据范围比较小,可以直接模拟,如果出现一次就加上去。
或者是直接map打表也可以
AC代码
暴力
class Solution {
public:
bool Check(int x, vector<int> &nums){
int cnt = 0;
for (auto y : nums)
if (x == y)
cnt ++;
return cnt == 1;
}
int sumOfUnique(vector<int>& nums) {
int ret = 0;
for (auto x : nums)
{
if (Check(x, nums))
ret += x;
}
return ret;
}
};
打表
class Solution {
public:
int sumOfUnique(vector<int>& nums) {
int ans = 0;
unordered_map<int, int> m;
for (auto x : nums)
m[x] ++;
for (auto [u, v] : m) // 注意这个用法
if (v == 1)
ans += u;
return ans;
}
};
B. 任意子数组和的绝对值的最大值
原题链接
https://leetcode-cn.com/problems/maximum-absolute-sum-of-any-subarray/
解题思路
- 解题主要从子串入手,求得的是区间和的绝对值,那么很可能会用到前缀和。
- 进一步分析以 R i R_i Ri为结尾子数组和绝对值最大时, L i L_i Li会有什么性质, m a x ( a b s ( P r e S u m R i − P r e S u m L i − 1 ) ) max(abs(PreSum_{R_i}- PreSum_{L_i-1})) max(abs(PreSumRi−PreSumLi−1)), P r e S u m R i PreSum_{R_i} PreSumRi是确定的,根据绝对值的性质,可知,最大值取出在 P r e S u m L i − 1 PreSum_{L_i-1} PreSumLi−1在 P r e S u m R i PreSum_{R_i} PreSumRi左侧最小,或 P r e S u m R i PreSum_{R_i} PreSumRi右侧最大,那么我直接保留出 P r e M a x , P r e M i n PreMax,PreMin PreMax,PreMin就可以线性处理出问题。
AC代码
class Solution {
public:
int maxAbsoluteSum(vector<int>& nums) {
int n = nums.size();
vector<int> pre(n + 5);
pre[0] = 0;
for (int i = 1; i <= n; i ++ ) pre[i] = pre[i - 1] + nums[i - 1];
int pre_max = 0, pre_min = 0;
int res = 0;
for (int i = 1; i <= n; i ++ ) // 对于右端点进行枚举
{
pre_max = max(pre_max, pre[i]); pre_min = min(pre_min, pre[i]);
res = max(res, max(abs(pre[i] - pre_max), abs(pre[i] - pre_min)));
}
return res;
}
};
C. 删除字符串两端相同字符后的最短长度
原题链接
https://leetcode-cn.com/problems/minimum-length-of-string-after-deleting-similar-ends/
解题思路
- 1.当两端字符不相同时,无法再次缩短
- 2.当两端字符相同时,那么一定会取完
-
- 倘若一次尚未全部取完,那么可能会导致左右两侧字符不等,形成1.的局面
AC代码
class Solution {
public:
int minimumLength(string s) {
int pre = 0, nxt = s.size() - 1;
char ch;
while (s[pre] == s[nxt] && pre < nxt) // 倘若只剩最后一个,是不行的
{
ch = s[pre];
while (s[pre] == ch && pre <= nxt) pre ++; // 注意是可以等于的
while (s[nxt] == ch && pre <= nxt) nxt --;
}
return nxt - pre + 1;
}
};
D. 最多可以参加的会议数目 II
原题链接
https://leetcode-cn.com/problems/maximum-number-of-events-that-can-be-attended-ii/
解题思路
倘若所有会议价值相同,仅仅有区间冲突,那么这就是一个贪心问题
只进行右端点排序,然后进行线性扫一遍数组,能加则加,因为我们是取最大数量的同时,使得结束时间尽可能的早。
当价值不同是,那么这是一个dp问题,思路类似于背包思路
AC代码
const int N = 1000010;
class Node
{
public:
int st, ed, value;
Node(int _st = 0, int _ed = 0, int _value = 0)
{
st = _st, ed = _ed, value = _value;
}
}q[N];
bool cmp(const Node &t1, const Node &t2)
{
if (t1.ed == t2.ed)
return t1.st < t2.st;
else
return t1.ed < t2.ed;
}
class Solution {
public:
int maxValue(vector<vector<int>>& events, int k) {
int n = events.size();
for (int i = 0; i < n; i ++ )
q[i + 1] = Node(events[i][0], events[i][1], events[i][2]);
sort(q + 1, q + 1 + n, cmp); // 排序
// vector<vector<int>(k + 5) > f(n + 5); // dp 数组
vector<vector<int>> f(n + 5, vector<int>(k + 5));
vector<int> pre(n + 5); // 预处理二分最近的不冲突区间
pre[1] = 0; // 注意这个是0,而不是 - 1
for (int i = 2; i <= n; i ++ )
{
static int l, r, mid, x;
x = q[i].st;
l = 1, r = i - 1;
// q[mid].ed < x 的最大值
if (q[1].ed >= x)
{
pre[i] = 0;
continue;
}
while (l < r)
{
mid = (l + r + 1) >> 1;
if (q[mid].ed < x)
l = mid;
else
r = mid - 1;
}
pre[i] = l;
}
// 进行dp 初始化,按道理说没有也是行的
for (int i = 0; i <= k; i ++ )
f[0][i] = 0;
/*
for (int i = 1; i <= n; i ++ )
f[i][0] = 0, f[i][1] = max(q[i].value, f[i-1][1]); // 这个地方容易写错,不如在下面初始化
*/
for (int i = 1; i <= n; i ++ )
{
for (int j = 1; j <= k; j ++ )
{
f[i][j] = f[i - 1][j]; // 不选 i
/// if (pre[i] == -1) continue;
f[i][j] = max(f[i][j], f[pre[i]][j - 1] + q[i].value); // 选了 i
}
}
return f[n][k];
}
};
心得
- auto [u, v] : m 很好使,简洁高效
- 处理子串(连续的)问题时,常常需要预处理区间,然后进行贪心,类似dp求解
- vector<vector> f(n + 5, vector(k + 5)); 有趣的写法