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/
解题思路
- 解题主要从子串入手,求得的是区间和的绝对值,那么很可能会用到前缀和。
- 进一步分析以RiR_iRi为结尾子数组和绝对值最大时,LiL_iLi会有什么性质,max(abs(PreSumRi−PreSumLi−1))max(abs(PreSum_{R_i}- PreSum_{L_i-1}))max(abs(PreSumRi−PreSumLi−1)), PreSumRiPreSum_{R_i}PreSumRi是确定的,根据绝对值的性质,可知,最大值取出在PreSumLi−1PreSum_{L_i-1}PreSumLi−1在PreSumRiPreSum_{R_i}PreSumRi左侧最小,或PreSumRiPreSum_{R_i}PreSumRi右侧最大,那么我直接保留出PreMax,PreMinPreMax,PreMinPreMax,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;elsereturn 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,而不是 - 1for (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;elser = 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)); 有趣的写法