第82场双周赛

黑暗后是黎明!

6116. 计算布尔二叉树的值

LeetCode第82场双周赛_单调栈

思路:

递归,就是对二叉树的递归遍历,判断非叶子节点是2还是3来进行与/并叶子的计算。

/**
* 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:
bool evaluateTree(TreeNode* root) {
//题目明确说了树中节点数目在 [1, 1000] 之间。所以不可能为空
//完整二叉树,无左孩子一定无有孩子,考虑一边就行
if(!root->left)return root->val;
auto l =evaluateTree(root->left);
auto r=evaluateTree(root->right);
if(root->val==2) return l||r;
return l&&r;
}
};

6117. 坐上公交的最晚时间

LeetCode第82场双周赛_操作数_02

思路:

来源灵佬https://www.bilibili.com/video/BV1Le4y1R7xu/?spm_id_from=333.788&vd_source=11a40407ca73e76119d9f9b789cc7ff2、

排序后,模拟乘客上车的过程。

模拟结束后:

如果最后一班公交还有空位,我们可以在发车时到达公交站,如果此刻有人,我们可以顺着他往前找到没人到达的时刻;
如果最后一班公交没有空位,我们可以找到上一个上车的乘客,顺着他往前找到一个没人到达的时刻。

作者:endlesscheng
链接:https://leetcode.cn/problems/the-latest-time-to-catch-a-bus/solution/pai-xu-by-endlesscheng-h9w9/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
public:
int latestTimeCatchTheBus(vector<int>& buses, vector<int>& passengers, int capacity) {
sort(buses.begin(), buses.end());
sort(passengers.begin(), passengers.end());
//j代表上车的人数,c代表当前的容量剩余
int j=0,c;
for(int t:buses){
//此时容量有剩余且当前的人数未到达全部乘客的人数,乘客来的比公交早
for(c=capacity;c&&j<passengers.size()&&passengers[j]<= t;j++){
c--;
}
}
j--;//代表最后一个上车的乘客
//顺着最后一个人往前找,找到即答案
int ans = c ? buses.back() : passengers[j];
while (j >= 0 && passengers[j--] == ans) --ans; // 往前找没人到达的时刻
return ans;
}
};

6118. 最小差值平方和

LeetCode第82场双周赛_算法_03

贪心 + 二分查找

参考:

​https://leetcode.cn/problems/minimum-sum-of-squared-difference/solution/c-by-lao-hang-n-a2sg/​

class Solution {
bool check(vector<int> diff, int mid, int cnt){
long long sum = 0; // sum注意要为long long,不然int类型存不下100000*100000
for(int i: diff) {
sum += (max(i, mid) - mid);
}
return sum<=cnt; // 操作数小于k1+k2说明大值都可以缩小到mid
}
public:
long long minSumSquareDiff(vector<int>& nums1, vector<int>& nums2, int k1, int k2) {
int n = nums1.size();
long long sum=0;
vector<int> diff(n);
for(int i=0;i<n;i++){
diff[i]=abs(nums1[i]-nums2[i]);
sum+= diff[i];
}
if(sum<=k1+k2) return 0;
//二分求目标值
int target, left = 0, right = 100001;
while(left<=right){
int mid = left +(right-left)/2;
if(check(diff,mid,k1+k2)){
target = mid;
right=mid-1;
}else{
left = mid+1;
}
}
//
int res=k1+k2;
// 将所有大于target的值变为target
for(int i = 0; i < n; ++i) {
if(diff[i]>target){
res-=(diff[i]-target); // 减去将该值变为target所需的操作数
diff[i]=target; // 更新该值为target
}
}
// 如果剩余操作数大于0,说明还可以继续操作差值数组
// 继续对大值们进行修改,此时数组中的大值均为target,那么就为target的项就减去1,操作数也减1
for(int i=0;i<n &&res>0;++i){
if(diff[i]==target){
--diff[i];
--res;
}
}
long long ans=0;
for(int i:diff){
ans+=1ll*i*i;
}
return ans;
}
};

​6119 元素值大于变化阈值的子数组​

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-apg2UPek-1657546486504)(…/…/AppData/Roaming/Typora/typora-user-images/image-20220711171604757.png)]

思路:

本题我的思路是,每次给一个子数组的长度,然后再遍历该长度下的子数组中所有元素是否能够全部符合题目条件,如果符合就把当前长度加入到一个set当中,最后遍历set的值即可。(好像不对,题目只要求有一个符合的即可)

别人的思路:

目前弄懂了单调栈的解法,还有并查集的方法目前还没有弄懂

/*
单调栈的应用:
不妨枚举nums[i]并假设某包含nums[i]的子段是长度为k的某段中最小的数字
在该段中其余数字都大于nums[i],只要nums[i]>threshold/k,那么段内的所有元素均大于threshold/k
我们只需要求出有没有这样的nums[i]就可以知道是否有符合题意的k
怎样维护某个nums[i]在某个段内是最小的数字?我们只需要找到nums[i]左边和右边首个严格小于nums[i]的索引
那么索引之间就是nums[i]这段的波及范围
快速求nums[i]左边和右边首个小于nums[i]的元素属于Next Greater问题,可以用单调栈解决
时间复杂度:O(N) 空间复杂度:O(N)
*/
class Solution {
public:
int validSubarraySize(vector<int>& nums, int threshold) {
int n=nums.size();
stack<int> s;
int left[n];// left[i] 为左侧小于 nums[i] 的最近元素位置(不存在时为 -1)
for(int i=0;i<n;i++){
// 遇到nums[i]<=nums[栈顶索引] -> 弹出栈顶索引直至nums[i]>nums[栈顶索引]
// 此时nums[栈顶索引]就是nums[i]左边首个严格小于nums[i]的数字
// 被弹出的那些栈顶元素是不可能成为后面left[i]有效取值的,因为会优先取到当前的nums[i]
// e.g. nums[st1]=[1,2,4,5] nums[i]=3 显然4与5不符合题意弹出 3才是符合题意的 加入后面有个6进来了
// 必然会优先取到3而不会取更前面的4与5
while(!s.empty() &&nums[s.top()] >=nums[i]){
s.pop();
}
left[i]=s.empty() ?-1:s.top(); // 栈顶的必定1严格小于nums[i]并且是最近的(-1表示取全部)
s.push(i);
}
int right[n];
s = stack<int>();
for(int i=n-1;i>=0;i--){
while(!s.empty() &&nums[s.top()] >=nums[i]){
s.pop();
}
right[i]=s.empty() ?n:s.top();
s.push(i);
}
// 枚举nums[i]根据其波及范围确定到k,再判断k是否合法
for(int i=0;i<n;i++){
int k = right[i]-left[i]-1;
// cout<<k;
if(nums[i]>threshold/k)return k;
}
return -1;
}
};

总结:
本次LeetCode周赛看了评论,普遍说难度大。对我来说也是,哈哈。很自闭,赛后看解析都很费力。
本次的题目的类型分别为 1.二叉树的应用(还行)2.脑筋急转弯 3.贪心+二分 4.并查集/单调栈
本次需要补的内容有:单调栈的内容,并查集的的内容以及类型的题目。重新回顾一下 3/4两题的解题方法。
在下周的时候重点学习一下相关欠缺的内容,绝不忙不刷题。