LeetCode第187场周赛(Weekly Contest 187)解题报告
差点又要掉分了,还好最后几分钟的时候,绝杀 AK。干巴爹!!!
第一题:思路 + 模拟暴力。
第二题:线性扫描。
第三题:双指针(滑动窗口) + 优先队列。
第四题:暴力每一行最小 k 个 + 优先队列 或者 直接二分数组和值。
详细题解如下。
1.旅行终点站
AC代码(C++)
2. 是否所有 1 都至少相隔 k 个元素
AC代码(C++)
3.绝对差不超过限制的最长连续子数组
AC代码(C++)
4.有序矩阵中的第 k 个最小数组和
AC代码(方法一 暴力 + 优先队列 C++)
AC代码(方法二 二分 C++)
LeetCode第187场周赛地址:
https://leetcode-cn.com/contest/weekly-contest-187/
1.旅行终点站
题目链接
https://leetcode-cn.com/problems/destination-city/
题意
给你一份旅游线路图,该线路图中的旅行线路用数组 paths 表示,其中 paths[i] = [cityAi, cityBi] 表示该线路将会从 cityAi 直接前往 cityBi 。请你找出这次旅行的终点站,即没有任何可以通往其他城市的线路的城市。
题目数据保证线路图会形成一条不存在循环的线路,因此只会有一个旅行终点站。
示例 1:
输入:paths = [["London","New York"],["New York","Lima"],["Lima","Sao Paulo"]] 输出:"Sao Paulo" 解释:从 "London" 出发,最后抵达终点站 "Sao Paulo" 。本次旅行的路线是 "London" -> "New York" -> "Lima" -> "Sao Paulo" 。
示例 2:
输入:paths = [["B","C"],["D","B"],["C","A"]] 输出:"A" 解释:所有可能的线路是: "D" -> "B" -> "C" -> "A". "B" -> "C" -> "A". "C" -> "A". "A". 显然,旅行终点站是 "A" 。
示例 3:
输入:paths = [["A","Z"]] 输出:"Z"
提示:
1 <= paths.length <= 100
paths[i].length == 2
1 <= cityAi.length, cityBi.length <= 10
cityAi != cityBi
所有字符串均由大小写英文字母和空格字符组成。
解题思路
根据题意,我们知道终点就是,一个城市无法到达其他城市。
因此,我们统计每一个城市可以是否可以到达其他城市
那么我们可以使用 map,记录一个城市,可以到达其他城市的数量(可以是重复达到,没关系,只要达到其他城市即可)
那么这样子,我们还需要统计总共有几个不同城市(为了后面可以枚举所有城市,是否可以到达其他城市),那么这个可以用 set
AC代码(C++)
class Solution {
public:string destCity(vector<vector<string>>& paths) {unordered_map<string, int> mp;unordered_set<string> diffCitys;mp.clear();diffCitys.clear();// 统计,每一个城市,可以达到其他城市的数量(无法达到,那就是 0),同时统计不同城市的数量for(auto p : paths){++mp[p[0]];diffCitys.insert(p[0]);diffCitys.insert(p[1]);}// 枚举每一个城市,当一个城市无法到达其他城市,那么这个城市就是 终点for(auto c : diffCitys){if(mp[c] == 0) return c;}return "";}
};
2. 是否所有 1 都至少相隔 k 个元素
题目链接
https://leetcode-cn.com/problems/check-if-all-1s-are-at-least-length-k-places-away/
题意
给你一个由若干
0
和1
组成的数组nums
以及整数k
。如果所有1
都至少相隔k
个元素,则返回True
;否则,返回False
。示例 1:
【示例有图,具体可以看链接】 输入:nums = [1,0,0,0,1,0,0,1], k = 2 输出:true 解释:每个 1 都至少相隔 2 个元素。
示例 2:
【示例有图,具体可以查看链接】 输入:nums = [1,0,0,1,0,1], k = 2 输出:false 解释:第二个 1 和第三个 1 之间只隔了 1 个元素。
提示:
1 <= nums.length <= 10^5
0 <= k <= nums.length
nums[i]
的值为0
或1
解题思路
根据题意,我们只要每一次遇到 1 ,那么就计算其与上一个 1 相差的间隔。
那么这样子,我们就需要去记录上一个 1 的位置,这样子就可以实现 线性扫描
比如 示例 1 的
[1,0,0,0,1,0,0,1]
先找到第一个 1 的下标 0
然后线性扫描,第二个 1 下标是 4 (此时上一个 1 的下标是 0)
然后此时往后继续扫描,而 上一个 1 的位置进行更新 为 4
第三个 1 的下标是 7(上一个 1 的下标是 4)
AC代码(C++)
class Solution {
public:bool kLengthApart(vector<int>& nums, int k) {int last = -1; // 记录上一个 1 的位置int n = nums.size();for(int i = 0; i < n; ++i){if(nums[i] == 1){last = i;break;}}for(int i = last + 1;i < n; ++i){if(nums[i] == 1){if(i - last - 1 < k) return false;last = i; // 更新}}return true;}
};
3.绝对差不超过限制的最长连续子数组
题目链接
https://leetcode-cn.com/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit/
题意
给你一个整数数组 nums ,和一个表示限制的整数 limit,请你返回最长连续子数组的长度,该子数组中的任意两个元素之间的绝对差必须小于或者等于 limit 。
如果不存在满足条件的子数组,则返回 0 。
示例 1:
输入:nums = [8,2,4,7], limit = 4 输出:2 解释:所有子数组如下: [8] 最大绝对差 |8-8| = 0 <= 4. [8,2] 最大绝对差 |8-2| = 6 > 4. [8,2,4] 最大绝对差 |8-2| = 6 > 4. [8,2,4,7] 最大绝对差 |8-2| = 6 > 4. [2] 最大绝对差 |2-2| = 0 <= 4. [2,4] 最大绝对差 |2-4| = 2 <= 4. [2,4,7] 最大绝对差 |2-7| = 5 > 4. [4] 最大绝对差 |4-4| = 0 <= 4. [4,7] 最大绝对差 |4-7| = 3 <= 4. [7] 最大绝对差 |7-7| = 0 <= 4. 因此,满足题意的最长子数组的长度为 2 。
示例 2:
输入:nums = [10,1,2,4,7,2], limit = 5 输出:4 解释:满足题意的最长子数组是 [2,4,7,2],其最大绝对差 |2-7| = 5 <= 5 。
示例 3:
输入:nums = [4,2,2,2,4,4,2,2], limit = 0 输出:3
提示:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^9
0 <= limit <= 10^9
解题分析
根据题意,我们先对 示例 1 进行分析
nums = [8,2,4,7], limit = 4
一开始范围是 [8],满足
然后 想继续扩大 [8, 2],发现加入 2 后就不满足了
因此,范围要变小 [2]
接着,继续扩大 [2, 4]
[2, 4, 7] 也就不满足了,那就就想着减少
[4, 7]
所以我们发现,其实相当于一个双指针,l 和 r 圈出了一个范围 [l, r],那么一开始,我们只想要 r 不断的变大,变大到 [l, r] 不再满足 时,r 就定了。然后 l 增大(因为 [l, r] 已经是最大了,同时 r 不能再扩大,那么就考虑 l 变大,区间变小,那么 后面 r 可以考虑继续变大)
所以发现,l 和 r 是单调增加,也就是遍历一次数组
那么,当 [l, r] 的时候,我们要知道 这个区间内的最大值和最小值
我考虑使用 优先队列 来记录
也就是,每一次 r 增大,我们就往优先队列中放数
当 l 增大,考虑其中的 最大值 和 最小值的 下标一定要再 [l, r] 这个范围里。
因此,优先队列中存的是一个 二元数,(val, index),下标 index 对应的 值 val。(其中,最大和最小是按 val 来排的)
时间复杂度是 O(n * logn)
AC代码(C++)
#define pii pair<int, int>
#define mk(x, y) make_pair(x, y)class Solution {
public:int longestSubarray(vector<int>& nums, int limit) {priority_queue<pii, vector<pii>, less<pii> > mx; // 放最大,因此要最大值在队列头priority_queue<pii, vector<pii>, greater<pii> > mi;int ans = 0;int n = nums.size();int r = 0;mx.push(mk(nums[0], 0));mi.push(mk(nums[0], 0));for(int i = 0;i < n; ++i) // 遍历 [i, r],遍历 左端点{// 要取出,最大值 和 最小值,一定要保证下标在这个范围里,不在范围里,就去掉while(!mx.empty() && mx.top().second < i) mx.pop();while(!mi.empty() && mi.top().second < i) mi.pop();// 如果队列都去完了,说明 [i, r] 中 i == r,那么此时是单独 i 这个下标的数应该在队列中if(mx.empty()) mx.push(mk(nums[i], i));if(mi.empty()) mi.push(mk(nums[i], i));int mxVal = mx.top().first, miVal = mi.top().first;while(r + 1 < n) // 尝试 尽可能把 r 增大{mxVal = max(mxVal, nums[r + 1]);miVal = min(miVal, nums[r + 1]);if(mxVal - miVal > limit) break;mx.push(mk(nums[r + 1], r + 1));mi.push(mk(nums[r + 1], r + 1));++r;}// cout << i << " " << r << endl;ans = max(ans, r - i + 1);}return ans;}
};
4.有序矩阵中的第 k 个最小数组和
题目链接
https://leetcode-cn.com/problems/find-the-kth-smallest-sum-of-a-matrix-with-sorted-rows/
题意
给你一个 m * n 的矩阵 mat,以及一个整数 k ,矩阵中的每一行都以非递减的顺序排列。
你可以从每一行中选出 1 个元素形成一个数组。返回所有可能数组中的第 k 个 最小 数组和。
示例 1:
输入:mat = [[1,3,11],[2,4,6]], k = 5 输出:7 解释:从每一行中选出一个元素,前 k 个和最小的数组分别是: [1,2], [1,4], [3,2], [3,4], [1,6]。其中第 5 个的和是 7 。
示例 3:
输入:mat = [[1,10,10],[1,4,5],[2,3,6]], k = 7 输出:9 解释:从每一行中选出一个元素,前 k 个和最小的数组分别是: [1,1,2], [1,1,3], [1,4,2], [1,4,3], [1,1,6], [1,5,2], [1,5,3]。其中第 7 个的和是 9 。
提示:
m == mat.length
n == mat.length[i]
1 <= m, n <= 40
1 <= k <= min(200, n ^ m)
1 <= mat[i][j] <= 5000
mat[i] 是一个非递减数组
解题分析
方法一、暴力 + 优先队列
我们一开始想到的是,暴力方法,也就是考虑每一行所有可能的累加后的数
问题是,这样子可能到了最后一行,就最大情况 总共有 40 ^ 40 个 数
但是我们发现,从数据范围来看, k 的最大值 为 200
那么,其实我们每一行只考虑 保留 最小的 k 个,那么下一行中,总共有 m * k 个数,又再取出 其中 k 个。
这样子我们每一行,最多考虑 m * k 个数,然后取出 最小 k 个 来进行保存即可
因此,我们利用一个 优先队列(也就是 最大堆),队列头 是 最大值,此时已经上一行 有了 最小的 k 个数。
那么我们 不断的取 k 个,和 这一行组合,那么总共有 m * k 个数
当 不够 k 个时,我们一直 放进队列。当超过 k 个时,我们 和 队列头进行比较,如果 比队列头要小,说明这个数才是 最小的 k 个中一个,因此队列头 出队,这个数进队。
时间复杂度是,O(n * m * k * logk),我们要枚举每一个数,然后 队列中最多 k 个数,因此总的时间复杂度,根据数据范围,不会超时
方法二、二分
我们枚举 数组和 的值,最大范围和最小范围 为 [第一列所有和, 最后一列所有和]
那么我们二分,得到 mid 后,去判断,这个 mid 值,在比 mid 小的有几个数
从而不断的减少范围
最后直接是得到 第 k 小 的那个 数组和 值。
AC代码(方法一 暴力 + 优先队列 C++)
class Solution {
public:int kthSmallest(vector<vector<int>>& mat, int k) {priority_queue<int> last, res; // (最大堆),队列头是最大值int n = mat.size(), m = mat[0].size();for(int i = 0;i < min(m, k); ++i) last.push(mat[0][i]); // 一开始,把 第一行 的前 k 个,放进队列for(int i = 1;i < n; ++i){int len = 0; // 记录此时 队列中的个数while(!last.empty()) // 把上一行的每一个队列都出来{int cur = last.top();last.pop();for(int j = 0;j < m; ++j){if(len < k){res.push(cur + mat[i][j]);++len;}else{if(res.top() > cur + mat[i][j]){res.pop();res.push(cur + mat[i][j]);}}}}while(!res.empty()) // 此时 last 是空,res 是有,但是我们last 是上一行的结果,因此把 res 的转移放到 last 中{int cur = res.top();res.pop();last.push(cur);}}return last.top(); // 最后是 最小 k 个中的最大值,也就是 队列头}
};
AC代码(方法二 二分 C++)
class Solution {
public:vector<vector<int>>temp;int m,n;int kthSmallest(vector<vector<int>>& mat, int k) {temp=mat;m=mat.size(),n=mat[0].size();int left=0,right=0;for(int i=0;i<m;i++) left+=mat[i][0],right+=mat[i].back();int init=left;while(left<right){int mid=(left+right)>>1;int num=1;dfs(mid,0,init,num,k);if(num>=k) right=mid;else left=mid+1;}return left;}void dfs(int mid,int index,int sum,int& num,int k){if(sum>mid||index==m||num>k) return;dfs(mid,index+1,sum,num,k);for(int i=1;i<n;i++){if(sum+temp[index][i]-temp[index][0]<=mid){num++;dfs(mid,index+1,sum+temp[index][i]-temp[index][0],num,k);}else{break;}}}
};
LeetCode第187场周赛(Weekly Contest 187)解题报告相关推荐
- 记LeetCode第143次周赛(Weekly Contest 143)
上午打完LeetCode第143次周赛,发现很多不常用的知识点都比较生涩了,最后一个半小时也只ac了前两题.这一次的题目相对以往还是比较简单吧,但奈何就是迟迟没有在代码上有较满意的实现.学习果然是不进 ...
- LeetCode第176场周赛(Weekly Contest 176)解题报告
又是一周掉分之旅,我发现,LeetCode周赛的数据好水,所以有的时候,实在没思路,先暴力解决试试(即使分析出时间复杂度会超时),比如第二题和第三题都可以暴力通过,GG思密达. 这周主要使用了数据结构 ...
- LeetCode第 227 场周赛题解
LeetCode第 227 场周赛题解 检查数组是否经排序和轮转得到 原题链接 https://leetcode-cn.com/problems/check-if-array-is-sorted-an ...
- LeetCode 第 194 场周赛
LeetCode 第 194 场周赛 数组异或操作 思路和代码 保证文件名唯一 思路及代码 避免洪水泛滥 思路及代码 找到最小生成树里的关键边和伪关键边 思路及代码 这次周赛比以往难很多. 数组异或操 ...
- Acwing第72场周赛+Leetcode第314场周赛
Acwing第72场周赛 第一题:AcWing 4624. 最小值 分析:向下取整可以用到math.h头文件中的floor()函数,最后输出时套用两个min()函数求三个数的最小值即可. 代码: #i ...
- Leetcode第 310 场周赛 补打
Leetcode 第310场周赛 自己赛后打了一下,记录了一下时间,大概15min A 3题,第四题是写不出来,然后学习了一天线段树(真的强). 思路: 1.排序后统计偶数的数目 2.遍历扫一遍,用直 ...
- Leetcode第321场周赛补题
Leetcode第321场周赛补题 第一题:6245. 找出中枢整数 - 力扣(LeetCode) 分析:由于数组中是差值为1的等差数列,所以可以直接用等差数列求和公式的朴素法更加简便的解决这题,,其 ...
- Leetcode 第133场周赛解题报告
今天参加了leetcode的周赛,算法比赛,要求速度比较快.有思路就立马启动,不会纠结是否有更好的方法或代码可读性.只要在算法复杂度数量级内,基本上是怎么实现快速就怎么来了. 比赛时先看的第二题,一看 ...
- [算法]LeetCode第194场周赛202006021
第194场周赛 20200621 1486. 数组异或操作 题目描述1 给你两个整数,n 和 start . 数组 nums 定义为:nums[i] = start + 2*i(下标从 0 开始)且 ...
最新文章
- python每隔2s执行一次hello world!
- Linux Malloc分析-从用户空间到内核空间
- python迭代数据类型_在大型数据集上自动迭代推断数据类型和最小项大小
- C语言学习及应用笔记之七:C语言中的回调函数及使用方式
- DWG TrueView 2010 下载地址
- android:图片裁剪
- 团队-科学计算器-代码设计规范
- 【数字信号】基于matlab GUI手机拨号音效模拟【含Matlab源码 909期】
- 用计算机制作模拟汽车,计算机模拟在汽车行业应用将兴起
- 解决jinjia2 for循环变量作用域问题
- 等效于35mm相机焦距的计算方法
- 如何让自己像打王者荣耀一样发了疯、拼了命、石乐志的学习?
- EMLOG模板 自适应Fontopen3 可做企业站
- Win10文件夹莫名其妙闪退
- 一毕业就上了艘“火箭”,这群校招生在大公司创业
- OpenWhisk部署指南
- Leetcode-数组-904
- 调用浏览器局部打印,空白、只有一页问题、火狐兼容
- python分析pdf年报 货币现金_利用python tushare pandas进行财报分析
- 华为[ENSP]用户界面的用户权限级别
热门文章
- 农产品电子商务行业调研报告 - 市场现状分析与发展前景预测
- 吴恩达Coursera深度学习课程 deeplearning.ai (4-4) 人脸识别和神经风格转换--编程作业
- 智能卡应用的开发流程
- 王者荣耀服务器维护8月23日,王者荣耀8月23日更新公告:其它系统优化及BUG修复...
- show master status 时没有数据显示
- jsMath对象中的三角函数
- extmail mysql数据库 重启_extmail数据库dspam表修复
- 简述sqlite数据库的特点_sqlite数据库的优缺点
- Elasticsearch中FST与前缀搜索
- 履 天泽履 乾上兑下