差点又要掉分了,还好最后几分钟的时候,绝杀 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)解题报告相关推荐

  1. 记LeetCode第143次周赛(Weekly Contest 143)

    上午打完LeetCode第143次周赛,发现很多不常用的知识点都比较生涩了,最后一个半小时也只ac了前两题.这一次的题目相对以往还是比较简单吧,但奈何就是迟迟没有在代码上有较满意的实现.学习果然是不进 ...

  2. LeetCode第176场周赛(Weekly Contest 176)解题报告

    又是一周掉分之旅,我发现,LeetCode周赛的数据好水,所以有的时候,实在没思路,先暴力解决试试(即使分析出时间复杂度会超时),比如第二题和第三题都可以暴力通过,GG思密达. 这周主要使用了数据结构 ...

  3. LeetCode第 227 场周赛题解

    LeetCode第 227 场周赛题解 检查数组是否经排序和轮转得到 原题链接 https://leetcode-cn.com/problems/check-if-array-is-sorted-an ...

  4. LeetCode 第 194 场周赛

    LeetCode 第 194 场周赛 数组异或操作 思路和代码 保证文件名唯一 思路及代码 避免洪水泛滥 思路及代码 找到最小生成树里的关键边和伪关键边 思路及代码 这次周赛比以往难很多. 数组异或操 ...

  5. Acwing第72场周赛+Leetcode第314场周赛

    Acwing第72场周赛 第一题:AcWing 4624. 最小值 分析:向下取整可以用到math.h头文件中的floor()函数,最后输出时套用两个min()函数求三个数的最小值即可. 代码: #i ...

  6. Leetcode第 310 场周赛 补打

    Leetcode 第310场周赛 自己赛后打了一下,记录了一下时间,大概15min A 3题,第四题是写不出来,然后学习了一天线段树(真的强). 思路: 1.排序后统计偶数的数目 2.遍历扫一遍,用直 ...

  7. Leetcode第321场周赛补题

    Leetcode第321场周赛补题 第一题:6245. 找出中枢整数 - 力扣(LeetCode) 分析:由于数组中是差值为1的等差数列,所以可以直接用等差数列求和公式的朴素法更加简便的解决这题,,其 ...

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

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

  9. [算法]LeetCode第194场周赛202006021

    第194场周赛 20200621 1486. 数组异或操作 题目描述1 给你两个整数,n 和 start . 数组 nums 定义为:nums[i] = start + 2*i(下标从 0 开始)且 ...

最新文章

  1. python每隔2s执行一次hello world!
  2. Linux Malloc分析-从用户空间到内核空间
  3. python迭代数据类型_在大型数据集上自动迭代推断数据类型和最小项大小
  4. C语言学习及应用笔记之七:C语言中的回调函数及使用方式
  5. DWG TrueView 2010 下载地址
  6. android:图片裁剪
  7. 团队-科学计算器-代码设计规范
  8. 【数字信号】基于matlab GUI手机拨号音效模拟【含Matlab源码 909期】
  9. 用计算机制作模拟汽车,计算机模拟在汽车行业应用将兴起
  10. 解决jinjia2 for循环变量作用域问题
  11. 等效于35mm相机焦距的计算方法
  12. 如何让自己像打王者荣耀一样发了疯、拼了命、石乐志的学习?
  13. EMLOG模板 自适应Fontopen3 可做企业站
  14. Win10文件夹莫名其妙闪退
  15. 一毕业就上了艘“火箭”,这群校招生在大公司创业
  16. OpenWhisk部署指南
  17. Leetcode-数组-904
  18. 调用浏览器局部打印,空白、只有一页问题、火狐兼容
  19. python分析pdf年报 货币现金_利用python tushare pandas进行财报分析
  20. 华为[ENSP]用户界面的用户权限级别

热门文章

  1. 农产品电子商务行业调研报告 - 市场现状分析与发展前景预测
  2. 吴恩达Coursera深度学习课程 deeplearning.ai (4-4) 人脸识别和神经风格转换--编程作业
  3. 智能卡应用的开发流程
  4. 王者荣耀服务器维护8月23日,王者荣耀8月23日更新公告:其它系统优化及BUG修复...
  5. show master status 时没有数据显示
  6. jsMath对象中的三角函数
  7. extmail mysql数据库 重启_extmail数据库dspam表修复
  8. 简述sqlite数据库的特点_sqlite数据库的优缺点
  9. Elasticsearch中FST与前缀搜索
  10. 履 天泽履 乾上兑下