之前收藏了极客时间的算法训练营3期 共21课,计划每一课写博客来记录学习,主要形式为
方法类型1
题1
题解
题2
题解
方法类型2
题1
题解
……
题目大体来自leetcode 和 acwing
主要记录和理解代码,所以基本完全搬运了视频题解代码,
个人学习感受体现在大致思路的总结和注释上。
第一题
动规的转移优化,优化第二层循环。
class Solution {
public:
int findMaxValueOfEquation(vector<vector<int>>& points, int k) {
deque<int> q;
int ans = -1e9;
//xi - xj <= k;
//xj >= xi -k;
//xj <= xi;
for (int i = 0; i < points.size(); i++) {
while (!q.empty() && points[q.front()][0] < points[i][0] - k) q.pop_front();
//更新答案
if (!q.empty())
ans = max(ans, points[q.front()][1] + points[i][1] + points[i][0] - points[q.front()][0]);
//维护单调性进队
//yj - xj 变化,保留最大
while(!q.empty() && points[q.back()][1] - points[q.back()][0] < points[i][1] - points[i][0]) q.pop_back();
q.push_back(i);
}
return ans;
}
};
第二题
滑动窗口做法,维护最大值
class Solution {
public:
int maxSubarraySumCircular(vector<int>& nums) {
int n = nums.size();
nums.insert(nums.begin(), 0);
vector<int> ss(2 * n + 1, 0);
for (int i = 1; i <= n; i++) {
ss[i] = ss[i - 1] + nums[i];
}
for (int i = n + 1; i <= 2 * n; i++) {
ss[i] = ss[i - 1] + nums[i - n];
}
deque<int> q;
int ans = -1e9;
for (int i = 1; i <= 2 * n; i++) {
while (!q.empty() && q.front() < i - n) q.pop_front();
if (!q.empty()) ans = max(ans, ss[i] - ss[q.front()]);
while (!q.empty() && ss[q.back()] >= ss[i]) q.pop_back();
q.push_back(i);
}
return ans;
}
};
特例解法,挖一块最小值 减去它 或者直接找到最大值。
class Solution {
public:
int maxSubarraySumCircular(vector<int>& nums) {
int n = nums.size();
nums.insert(nums.begin(), 0);
vector<int> s(n + 1, 0);
s[0] = 0;
for (int i = 1; i <= n; i++) {
s[i] = s[i - 1] + nums[i];
}
int ans = -1e9;
int tmp = 1e9;
for (int i = 1; i <= n; i++) {
tmp = min(tmp, s[i - 1]);
ans = max(ans, s[i] - tmp);
}
tmp = -1e9;
int ansMin = 1e9;
for (int i = 2; i <= n; i++) {
tmp = max(tmp, s[i - 1]);
ansMin = min(ansMin, s[i] - tmp);
}
for (int i = 1; i < n; i++) ansMin = min(ansMin, s[i]);
return max(ans, s[n] - ansMin);
}
};
第三题
区间内,最后一个戳哪个。
循环进行,需要考虑遍历每个长度。
class Solution {
public:
int maxCoins(vector<int>& nums) {
int n = nums.size();
nums.insert(nums.begin(), 1);
nums.push_back(1);
// 1 - n是有效数组
vector<vector<int>> f(n + 2, vector<int>(n + 2, 0));
for (int len = 1; len <= n; len++) {
for (int l = 1; l <= n + 1 - len; l++){
int r = l + len - 1;
for (int p = l; p <= r; p++) {
f[l][r] = max(f[l][r], f[l][p - 1] + f[p + 1][r] + nums[l - 1] * nums[r + 1] * nums[p]);
}
}
}
return f[1][n];
}
};
第四题
缺少量就加状态,
三个参数,两个规定区间,第三个是区间内有几堆。
class Solution {
public:
int mergeStones(vector<int>& stones, int k) {
int n = stones.size();
vector<vector<vector<int>>> f(n, vector<vector<int>>(n, vector<int>(k + 1, 1e9)));
for (int i = 0; i < n; i++) f[i][i][1] = 0;
for (int len = 2; len <= n; len++) {
for (int l = 0; l <= n - len; l++) {
int r = l + len - 1;
for (int i = 2; i <= k; i++) {
for (int p = l; p < r; p++)
f[l][r][i] = min(f[l][r][i], f[l][p][1] + f[p + 1][r][i - 1]);
}
int sum = 0;
for (int i = l; i <= r; i++) sum += stones[i];
f[l][r][1] = min(f[l][r][1], f[l][r][k] + sum);
}
}
return f[0][n - 1][1] == 1e9 ? -1 : f[0][n - 1][1];
}
};
第五题
树形动规,在处理孩子是否存在的问题上,使用了对空指针赋值的方法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int rob(TreeNode* root) {
h[nullptr] = {0, 0};
dfs(root);
return max(h[root][0], h[root][1]);
}
private:
void dfs(TreeNode* root) {
if (root == nullptr) return;
h[root] = {0, 0};
dfs(root->left);
dfs(root->right);
h[root][0] = max(h[root->left][0], h[root->left][1])
+ max(h[root->right][1], h[root->right][0]);
h[root][1] = h[root->left][0] + h[root->right][0] + root->val;
}
unordered_map<TreeNode*, vector<int>> h;
};