Leetcode第 217 场周赛

比赛链接:点这里

做完前两题我就知道今天的竞赛我已经结束了

这场比赛思维量还是比较大的。

1673. 找出最具竞争力的子序列

题目

给你一个整数数组 nums 和一个正整数 k ,返回长度为 k 且最具 竞争力nums 子序列。

数组的子序列是从数组中删除一些元素(可能不删除元素)得到的序列。

在子序列 a 和子序列 b 第一个不相同的位置上,如果 a 中的数字小于 b 中对应的数字,那么我们称子序列 a 比子序列 b(相同长度下)更具 竞争力 。 例如,[1,3,4][1,3,5] 更具竞争力,在第一个不相同的位置,也就是最后一个位置上, 4 小于 5

示例 1:

输入:nums = [3,5,2,6], k = 2
输出:[2,6]
解释:在所有可能的子序列集合 {[3,5], [3,2], [3,6], [5,2], [5,6], [2,6]} 中,[2,6] 最具竞争力。

示例 2:

输入:nums = [2,4,3,3,5,4,9,6], k = 4
输出:[2,3,3,4]

提示:

  • \(1 <= nums.length <= 10^5\)
  • \(0 <= nums[i] <= 10^9\)
  • \(1 <= k <= nums.length\)

思路

比赛时想到了单调栈,但是不熟不敢写( 还得练练。。

随便写了一通居然过了。

思路是找到数组中的最小值mi,那么最后的结果中肯定有这个值,如果mi所在的位置p之后的元素个数不足k个了,那么p之后的元素肯定都是要的,因为没有比它们更小的了,但是此时元素个数不够,那么就需要在p之前的元素的中找剩下的元素。这不就是个子问题么,直接递归完事儿。

代码

class Solution {
public:
vector<int> vis;
void helper(vector<int>& nums, int k, int l, int r){
if(l > r || k <= 0) return; // 最小值
int p;
int minn = 0x3f3f3f3f;
for(int i = l; i <= r; i++){
if(minn > nums[i]){
minn = nums[i];
p = i;
}
}
vis[p] = 1;
int t = r-p;
// 剩下元素个数大于k,直接在k后面找剩下的元素,因为此位置一定是最小的。
if(t >= k) {
helper(nums, k-1, p+1, r);
}
else {
//剩下元素没有k个
for(int i = p+1; i <= r; i++) vis[i] = 1;
helper(nums, k-t-1, l, p-1);
}
}
vector<int> mostCompetitive(vector<int>& nums, int k) {
if(!nums.size() || nums.size() < k) return vector<int>();
if(k == nums.size()) return nums; vector<int> t = nums;
sort(t.begin(), t.end());
if(t == nums){
return vector<int> (nums.begin(), nums.begin()+k);
} vis = vector<int> (nums.size(), 0); //记录答案 helper(nums, k, 0, nums.size()-1);
vector<int> ans;
for(int i = 0; i < nums.size(); i++){
if(vis[i]) ans.push_back(nums[i]);
}
return ans;
}
};

正解

单调栈维护栈顶元素比当前元素小。

弹栈当且仅当栈顶元素比当前元素大并且保证栈中元素加上剩余元素能够凑够k个。

巨坑:vector .size()方法一定要加(int), 这个错误不好找,也想不到。

以示警醒。

代码逻辑还是比较简单的。

代码

class Solution {
public:
vector<int> mostCompetitive(vector<int>& nums, int k) {
vector<int> st;
for(int i = 0; i < nums.size(); i++){
int c = nums[i];
while(!st.empty() && st.back() > c && k - (int)st.size() + 1 <= (int)nums.size() - i) st.pop_back();
st.push_back(c);
} while(st.size() > k) st.pop_back();
return st;
}
};

PS:这道题跟402. 移掉K位数字这道有异曲同工之妙,不同点在于一个是删除k个数,一个是留下k个数,但是思路大题一致,可以对比着来学。是的,做过的题记不住。ε=(´ο`*)))

1674. 使数组互补的最少操作次数

题目

给你一个长度为 偶数 n 的整数数组 nums 和一个整数 limit 。每一次操作,你可以将 nums 中的任何整数替换为 1limit 之间的另一个整数。

如果对于所有下标 i下标从 0 开始),nums[i] + nums[n - 1 - i] 都等于同一个数,则数组 nums互补的 。例如,数组 [1,2,3,4] 是互补的,因为对于所有下标 inums[i] + nums[n - 1 - i] = 5

返回使数组 互补最少 操作次数。

示例 1:

输入:nums = [1,2,4,3], limit = 4
输出:1
解释:经过 1 次操作,你可以将数组 nums 变成 [1,2,2,3](加粗元素是变更的数字):
nums[0] + nums[3] = 1 + 3 = 4.
nums[1] + nums[2] = 2 + 2 = 4.
nums[2] + nums[1] = 2 + 2 = 4.
nums[3] + nums[0] = 3 + 1 = 4.
对于每个 i ,nums[i] + nums[n-1-i] = 4 ,所以 nums 是互补的。

示例 2:

输入:nums = [1,2,2,1], limit = 2
输出:2
解释:经过 2 次操作,你可以将数组 nums 变成 [2,2,2,2] 。你不能将任何数字变更为 3 ,因为 3 > limit 。

示例 3:

输入:nums = [1,2,1,2], limit = 2
输出:0
解释:nums 已经是互补的。

提示:

  • \(n == nums.length\)
  • \(2 <= n <= 10^5\)
  • \(1 <= nums[i] <= limit <= 10^5\)
  • \(n\) 是偶数。

思路

完全没有思路,题刷得还是太少了啊

差分+前缀和

首先我们知道答案最大是\(n\), 这是将所有的数字都改了的情况。

假设数组中互补的两元素为\(A\)和\(B\),

我们用\(delta[i]\) 表示将\(A+B\)改为\(i\)时需要的操作次数,那么就可以分为以下五种情况:

  • \(2<=i<=min(A, B)\): 需要两次操作
  • \(min(A, B)+1<=i<=max(A, B) + 1\): 需要一次操作
  • \(i == A+B\): 不需要操作
  • \(A+B+1<=i<=limit + max(A, B)\): 需要一次操作
  • \(i > limit + max(A, B)\): 需要两次操作

但是知道了这么多种情况,怎么来表示他们之间的变化以及求和呢?

答案是使用差分数组,只需要改变被改变区间的边界的两个值,就可以在\(O(n)\)的时间里求和。

代码

class Solution {
public:
int minMoves(vector<int>& nums, int limit) {
vector<int> delta(limit*2+20, 0);
int n = nums.size();
delta[0] = n; //开始为n次操作
unordered_map<int, int> freq;
for(int i = 0; i < n/2; i++){
int sums = nums[i] + nums[n-i-1];
int lo = 1 + min(nums[i], nums[n-i-1]);
int hi = limit + 1 + max(nums[i], nums[n-i-1]); // 差分
delta[lo] --;
delta[sums]--;
delta[sums+1] ++;
delta[hi]++;
} //求和
for(int i = 1; i <= limit*2; i++) delta[i] += delta[i-1];
int ans = 0x3f3f3f3f;
for(int i = 1; i <= limit*2; i++){
ans = min(ans, delta[i]);
}
return ans;
}
};

1675. 数组的最小偏移量

题目

给你一个由 n 个正整数组成的数组 nums

你可以对数组的任意元素执行任意次数的两类操作:

  • 如果元素是偶数,除以\(2\)

    • 例如,如果数组是 [1,2,3,4] ,那么你可以对最后一个元素执行此操作,使其变成 [1,2,3, 2]
  • 如果元素是奇数,乘上\(2\)
    • 例如,如果数组是 [1,2,3,4] ,那么你可以对第一个元素执行此操作,使其变成 [2,2,3,4]

数组的 偏移量 是数组中任意两个元素之间的 最大差值

返回数组在执行某些操作之后可以拥有的 最小偏移量

示例 1:

输入:nums = [1,2,3,4]
输出:1
解释:你可以将数组转换为 [1,2,3,2],然后转换成 [2,2,3,2],偏移量是 3 - 2 = 1

示例 2:

输入:nums = [4,1,5,20,3]
输出:3
解释:两次操作后,你可以将数组转换为 [4,2,5,5,3],偏移量是 5 - 2 = 3

示例 3:

输入:nums = [2,10,8]
输出:3

提示:

  • \(n == nums.length\)
  • \(2 <= n <= 105\)
  • \(1 <= nums[i] <= 10^9\)

思路

又是智商被吊打的一题

优先队列

我们考虑维护大根堆,将所有数先化为最大的形式(也就是奇数\(*2\)),加入大根堆,维护 \(mi\) 表示当前堆的最小值。

之后我们不断地取出堆顶,也就是当前堆最大的数,除\(2\),重新加入大根堆,此过程中不断更新答案。

当堆顶为奇数时,就说明不能再除以\(2\),最大值不可能再缩小,答案也就不会再被缩小。

代码

class Solution {
public:
int minimumDeviation(vector<int>& nums) {
priority_queue<int> q; //大根堆
int mi = INT_MAX;
for(auto x : nums){
if(x&1) x <<= 1; //奇数乘2加入
q.push(x);
mi = min(mi, x);
}
int ans = INT_MAX;
while(1){
auto x = q.top();
q.pop();
ans = min(ans, x-mi);
if(x&1) break; //最大值奇数直接退出
x >>= 1;
q.push(x);
mi = min(x, mi); //维护mi为当前堆中的最小值
}
return ans;
}
};

Leetcode第 217 场周赛(思维量比较大)的更多相关文章

  1. LeetCode 第 165 场周赛

    LeetCode 第 165 场周赛 5275. 找出井字棋的获胜者 5276. 不浪费原料的汉堡制作方案 5277. 统计全为 1 的正方形子矩阵 5278. 分割回文串 III C 暴力做的,只能 ...

  2. Leetcode 第133场周赛解题报告

    今天参加了leetcode的周赛,算法比赛,要求速度比较快.有思路就立马启动,不会纠结是否有更好的方法或代码可读性.只要在算法复杂度数量级内,基本上是怎么实现快速就怎么来了. 比赛时先看的第二题,一看 ...

  3. LeetCode第151场周赛(Java)

    这是我第一次写周赛的题目,而且还是虚拟的.从这次起,以后就将所有错过的题目都写到博客来.当然既然是我错的,那代码肯定不是我自己的.我会注明来源.并且我会自己敲一遍.多总结总是没坏处的. 另外比较糟糕的 ...

  4. LeetCode第152场周赛(Java)

    这算是我第一次正式参加 LeetCode 的周赛吧.通过两道题.意料之中(通过上次模拟可以看出来).总的来说,脑袋还是不太灵光.想的有点慢.全球第一名 0:10:19 就全部通过...感觉我的智商被狠 ...

  5. Leetcode 第136场周赛解题报告

    周日的比赛的时候正在外面办事,没有参加.赛后看了下题目,几道题除了表面要考的内容,还是有些能发散扩展的地方. 做题目不是最终目的,通过做题发现知识盲区,去研究学习,才能不断提高. 理论和实际是有关系的 ...

  6. Leetcode 第137场周赛解题报告

    今天的比赛的题目相对来说比较「直白」,不像前几周都是一些特定的算法,如果你没学过不可能想出来. 做了这些周,对leetcode比赛的题目也发现了一些「规律」. 一般前两道题都很「简单」,只要有想法,直 ...

  7. LeetCode 第 150 场周赛

    一.拼写单词(LeetCode-1160) 1.1 题目描述 1.2 解题思路 由于给定的chars,每个字母只能用一次,所以用大小为26的数组charsArray来表示a-z(例如charsArra ...

  8. LeetCode 第 196 场周赛 (题目:5452-5455,这是参加过最坑的周赛,暴力n^2居然可以过)

    5452. 判断能否形成等差数列   给你一个数字数组 arr . 如果一个数列中,任意相邻两项的差总等于同一个常数,那么这个数列就称为 等差数列 . 如果可以重新排列数组形成等差数列,请返回 tru ...

  9. Leetcode 第135场周赛解题报告

    这周比赛的题目很有特点.几道题都需要找到一定的技巧才能巧妙解决,和以往靠数据结构的题目不太一样. 就是如果懂原理,代码会很简单,如果暴力做,也能做出来,但是十分容易出错. 第四题还挺难想的,想了好久才 ...

随机推荐

  1. 水题挑战4: luogu P1280 尼克的任务

    题目描述 尼克每天上班之前都连接上英特网,接收他的上司发来的邮件,这些邮件包含了尼克主管的部门当天要完成的全部任务,每个任务由一个开始时刻与一个持续时间构成. 尼克的一个工作日为 \(n\) 分钟,从 ...

  2. ERP的财务凭证的操作与设计--开源软件诞生23

    赤龙ERP财务凭证讲解--第23篇 用日志记录"开源软件"的诞生 [进入地址 点亮星星]----祈盼着一个鼓励 博主开源地址: 码云:https://gitee.com/redra ...

  3. PriorityQueue原理分析——基于源码

    在业务场景中,处理一个任务队列,可能需要依照某种优先级顺序,这时,Java中的PriorityQueue(优先队列)便可以派上用场.优先队列的原理与堆排序密不可分,可以参考我之前的一篇博客: 堆排序总 ...

  4. 对ESP8266的例子进行编译时报错check_python_dependencies的问题的解决

    尝试对ESP8266的例子进行编译时报错: make: *** 没有规则可制作目标"check_python_dependencies" 解决方法: 1.安装python pip包 ...

  5. servlet的简单介绍

    servlet简介: Servlet是一种Web服务器端编程技术,实现了特殊接口的Java类. 由支持Servlet的Web服务器调用和启动运行. 一个Servlet负责对应的一个或一组URL访问请求 ...

  6. 强迫自己学习Jquery三

    元素定位问题 offset 和 position必须要好好看一下,

  7. 四、API Gateway相关------微服务构架设计模式

  8. 响应式关系数据库处理R2DBC

    目录 简介 R2DBC介绍 项目依赖 创建ConnectionFactory 创建Entity Bean 初始化数据库 获取所有的用户 Prepare Statement 事务处理 WebFlux使用 ...

  9. 图解 Spring 循环依赖,写得太好了!

    Spring如何解决的循环依赖,是近两年流行起来的一道Java面试题. 其实笔者本人对这类框架源码题还是持一定的怀疑态度的. 如果笔者作为面试官,可能会问一些诸如"如果注入的属性为null, ...

  10. kettle——作业

    使用作业执行之前的转换,并且额外在表student2中添加一条数据 这里操作类似hue (1)新建一个作业,拉取组件 选择start 组件名字,类型可以下拉如图,根据需要选择即可 选择转换 并将sta ...