Leetcode第 217 场周赛(思维量比较大)
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
中的任何整数替换为 1
到 limit
之间的另一个整数。
如果对于所有下标 i
(下标从 0
开始),nums[i] + nums[n - 1 - i]
都等于同一个数,则数组 nums
是 互补的 。例如,数组 [1,2,3,4]
是互补的,因为对于所有下标 i
,nums[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 场周赛(思维量比较大)的更多相关文章
- LeetCode 第 165 场周赛
LeetCode 第 165 场周赛 5275. 找出井字棋的获胜者 5276. 不浪费原料的汉堡制作方案 5277. 统计全为 1 的正方形子矩阵 5278. 分割回文串 III C 暴力做的,只能 ...
- Leetcode 第133场周赛解题报告
今天参加了leetcode的周赛,算法比赛,要求速度比较快.有思路就立马启动,不会纠结是否有更好的方法或代码可读性.只要在算法复杂度数量级内,基本上是怎么实现快速就怎么来了. 比赛时先看的第二题,一看 ...
- LeetCode第151场周赛(Java)
这是我第一次写周赛的题目,而且还是虚拟的.从这次起,以后就将所有错过的题目都写到博客来.当然既然是我错的,那代码肯定不是我自己的.我会注明来源.并且我会自己敲一遍.多总结总是没坏处的. 另外比较糟糕的 ...
- LeetCode第152场周赛(Java)
这算是我第一次正式参加 LeetCode 的周赛吧.通过两道题.意料之中(通过上次模拟可以看出来).总的来说,脑袋还是不太灵光.想的有点慢.全球第一名 0:10:19 就全部通过...感觉我的智商被狠 ...
- Leetcode 第136场周赛解题报告
周日的比赛的时候正在外面办事,没有参加.赛后看了下题目,几道题除了表面要考的内容,还是有些能发散扩展的地方. 做题目不是最终目的,通过做题发现知识盲区,去研究学习,才能不断提高. 理论和实际是有关系的 ...
- Leetcode 第137场周赛解题报告
今天的比赛的题目相对来说比较「直白」,不像前几周都是一些特定的算法,如果你没学过不可能想出来. 做了这些周,对leetcode比赛的题目也发现了一些「规律」. 一般前两道题都很「简单」,只要有想法,直 ...
- LeetCode 第 150 场周赛
一.拼写单词(LeetCode-1160) 1.1 题目描述 1.2 解题思路 由于给定的chars,每个字母只能用一次,所以用大小为26的数组charsArray来表示a-z(例如charsArra ...
- LeetCode 第 196 场周赛 (题目:5452-5455,这是参加过最坑的周赛,暴力n^2居然可以过)
5452. 判断能否形成等差数列 给你一个数字数组 arr . 如果一个数列中,任意相邻两项的差总等于同一个常数,那么这个数列就称为 等差数列 . 如果可以重新排列数组形成等差数列,请返回 tru ...
- Leetcode 第135场周赛解题报告
这周比赛的题目很有特点.几道题都需要找到一定的技巧才能巧妙解决,和以往靠数据结构的题目不太一样. 就是如果懂原理,代码会很简单,如果暴力做,也能做出来,但是十分容易出错. 第四题还挺难想的,想了好久才 ...
随机推荐
- 基于synchronized锁的深度解析
1. 问题引入 小伙伴们都接触过线程,也都会使用线程,今天我们要讲的是线程安全相关的内容,在这之前我们先来看一个简单的代码案例. 代码案例: /** * @url: i-code.online * @ ...
- 4 urllib和urllib2的区别
4 urllib和urllib2的区别 这个面试官确实问过,当时答的urllib2可以Post而urllib不可以. urllib提供urlencode方法用来GET查询字符串的产生,而urllib2 ...
- 7 apache和nginx的区别
7 apache和nginx的区别 nginx 相对 apache 的优点: 轻量级,同样起web 服务,比apache 占用更少的内存及资源 抗并发,nginx 处理请求是异步非阻塞的,支持更多的并 ...
- 记一次ns3的安装过程
官方安装教程:https://www.nsnam.org/wiki/Installation 推荐使用Ubuntu18.04,Ubuntu20.04有些依赖无法下载. 准备工作 # 如果下载速度很慢, ...
- mysql between and 是[a,b]闭区间
mysql between and 是[a,b]闭区间 mysql between and 是[a,b]闭区间
- 强迫自己学Jquery
Jquery 适合我吗? 里面有很多用不上的功能, 我需要什么,常用的判断,常用的扩展,来自Dojo的Hitch函数,事件绑定,样式处理(添加 删除 替换),Ajax,模块加载, 足够了,真心不想用这 ...
- struts.xml中的配置内容
一些常量的配置 包标签 拦截器标签(自定义拦截器,拦截器栈) //对待拦截器栈与拦截器是一样的,只是标签不同而已. global-results标签 action标签:拦截器标签,resu ...
- HTTP介绍(一)
超文本传输协议(HTTP)是一种用于分布式,协作式超媒体信息系统的应用程序层协议.HTTP是万维网(World Wide Web)数据通信的基础,超文本文档包括指向用户可以轻松访问的其他资源的超链接, ...
- day93:flask:
目录 1.HTTP的会话控制 2.Cookie 3.Session 4.请求钩子 5.捕获错误 6.上下文:context 7.Flask-Script 1.HTTP的会话控制 1.什么是会话控制? ...
- 2018.1.15复习_ css+js
[1]几个常见的css标签:--------------------------------------------------background-color; 设置背景颜色background-p ...