这次是第一次参加周赛,在参加之前看了上一场的周赛题目,发现最多也就Medium 难度。
而这次的题目还是算简单的,在比赛时由于中途有事走了,时间内完成两题,后两题是晚上补完的。
题目有点绕,比如第一题,一开始我以为是一个求LIS的问题,然后最后发现其实很简单,哈哈哈。
第三题的示例有点问题,在写完代码之后运行出错,发现第四个示例出了问题。所以有时候代码还是要根据具体题目来进行微小修改的。
LeetCode第138场周赛地址:
https://leetcode-cn.com/contest/weekly-contest-138/
https://leetcode-cn.com/contest/weekly-contest-138/problems/height-checker/
学校在拍年度纪念照时,一般要求学生按照 非递减 的高度顺序排列。
请你返回至少有多少个学生没有站在正确位置数量。该人数指的是:能让所有学生以 非递减 高度排列的必要移动人数。
示例:
输入:[1,1,4,2,1,3] 输出:3 解释: 高度为 4、3 和最后一个 1 的学生,没有站在正确的位置。
提示:
1 <= heights.length <= 100
1 <= heights[i] <= 100
其实很简单,就是本来应该是一个非递减数组,求有几个学生没有站到正确位置。
将数组排序,然后利用排序数组和原本的数组进行比较,如果该位置数值不相等,说明站错位了。
时间复杂度是 O(nlogn),不会超时。
vector 的使用,可以看我的另一篇博客:C++的vector用法
class Solution {
public:
int heightChecker(vector& heights) {
vector sortH(heights);
sort(sortH.begin(),sortH.end());
int ans = 0;
for(int i = 0;i < sortH.size();i++)
{
if(sortH[i] != heights[i])
++ans;
}
return ans;
}
};
https://leetcode-cn.com/contest/weekly-contest-138/problems/grumpy-bookstore-owner/
今天,书店老板有一家店打算试营业
customers.length
分钟。每分钟都有一些顾客(customers[i]
)会进入书店,所有这些顾客都会在那一分钟结束后离开。在某些时候,书店老板会生气。 如果书店老板在第
i
分钟生气,那么grumpy[i] = 1
,否则grumpy[i] = 0
。 当书店老板生气时,那一分钟的顾客就会不满意,不生气则他们是满意的。书店老板知道一个秘密技巧,能抑制自己的情绪,可以让自己连续
X
分钟不生气,但却只能使用一次。请你返回这一天营业下来,最多有多少客户能够感到满意的数量。
示例:
输入:customers = [1,0,1,2,1,1,7,5], grumpy = [0,1,0,1,0,1,0,1], X = 3 输出:16 解释: 书店老板在最后 3 分钟保持冷静。 感到满意的最大客户数量 = 1 + 1 + 1 + 1 + 7 + 5 = 16.
提示:
1 <= X <= customers.length == grumpy.length <= 20000
0 <= customers[i] <= 1000
0 <= grumpy[i] <= 1
其实就是两个数组,然后第一个数组表示客户数量,第二个数组表示情绪。
sum=customers[i]*(1-grumpy[i] ) (0 <= i < n)
然后给定一个数X(1<=X<=n),可以把grumpy数组中连续X长度的元素的值从1变为0。问在确定X值后sum的最大值是多少?
做两步操作
第一步:先将情绪数组中0 的对应位置的客户数都加起来,同时将对应位置的客户数变为0。
第二步:现在就是将 第一个数组(已经被求和的位置的数值变为0)的连续 X 长度的值求出来,找出里面的最大值。
最后返回值就是 第一步值+第二步中的最大值。
第一步的复杂度是 O(n),第二步复杂度本来两层循环是 O(n^2),这样子会超时。
但第二步可以变为O(n),具体是先求出最开始前X个和,然后后一个X之和,是加上新的位置并减去最开始的那个位置数值。这样子可以只用遍历一次数组即可。
因为第二步,需要有前X个之和,所以一开始我们可以给一个边界条件,就是当 数组长度<=X,说明此时可以将第二个数组全变为0,所以就是返回第一个数组所有元素求和。
class Solution {
public:
int maxSatisfied(vector& customers, vector& grumpy, int X) {
int ans = 0;
if(customers.size() <= X)
{
for(int i = 0;i < customers.size();i++)
{
ans += customers[i];
}
return ans;
}
for(int i = 0;i < customers.size();i++)
{
if(grumpy[i]==0)
{
ans += customers[i];
customers[i] = 0;
}
}
int maxX = -1;
int sum = 0;
for(int i = 0;i < X;i++)
sum += customers[i];
for(int i = X;i < customers.size();i++)
{
if(sum > maxX)
maxX = sum;
sum += customers[i];
sum -= customers[i-X];
}
if(sum > maxX)
maxX = sum;
return ans+maxX;
}
};
https://leetcode-cn.com/contest/weekly-contest-138/problems/previous-permutation-with-one-swap/
给你一个正整数的数组
A
(其中的元素不一定完全不同),请你返回可在 一次交换(交换两数字A[i]
和A[j]
的位置)后得到的、按字典序排列小于A
的最大可能排列。如果无法这么操作,就请返回原数组。
示例 1:
输入:[3,2,1] 输出:[3,1,2] 解释: 交换 2 和 1
示例 4:
输入:[3,1,1,3] 输出:[1,1,3,3]
提示:
1 <= A.length <= 10000
1 <= A[i] <= 10000
题目简单直白,题意也比较好理解。直交换两个数值,得到小于当前字典序的集合中,最大的那个字典序排列。
第一步:从当前序列的后往前找,找到第一个降序的位置(A[i]>A[i+1]),则必存在能构造比当前小的序列。
第二步:把A[i]后面的数字中,小于A[i]且最接近A[i]的值的数字找出来,和A[i]交换。第二步的做法,从第一步找出 i 的位置之后,用两个下标分别指向 i 和 i+1。然后找到一个值如果 i+1 的大,那这个下标值就移动到此时的位置。
为什么第一步不再往前找,因为往前找更换,会让小的值出现在高位,导致不是最大字典序。
为什么第二步要找最接近的且小的,因为大的更换就超过当前序列,只能找小的。从小的里面找个最大值更换才能字典序最大,且不会超过当前序。
题目中的示例4是有问题的,答案应该是比[3,1,1,3]小的最大字典序是[1,3,1,3],而不是[1,1,3,3]。
但是为了把题目过了,只能把16行改为 if(A[i]>=A[e] && A[i]
,多加个等号。所以有的时候,程序还是要为了完成出的题而进行适当修改的。
class Solution {
public:
vector prevPermOpt1(vector& A) {
int s = 0,e = 0; // 定义初始值,不然如果出现没有交换的情况,那么s与e 就没有初值,导致运行错误
for(int i = A.size()-2;i>=0;--i)
{
if(A[i] > A[i+1])
{
s = i;
e = i+1;
break;
}
}
for(int i = s+2;i < A.size();i++)
{
if(A[i]>=A[e] && A[i]
https://leetcode-cn.com/contest/weekly-contest-138/problems/distant-barcodes/
在一个仓库里,有一排条形码,其中第
i
个条形码为barcodes[i]
。请你重新排列这些条形码,使其中两个相邻的条形码 不能 相等。 你可以返回任何满足该要求的答案,此题保证存在答案。
示例 1:
输入:[1,1,1,2,2,2] 输出:[2,1,2,1,2,1]
示例 2:
输入:[1,1,1,1,2,2,3,3] 输出:[1,3,1,3,2,1,2,1]
提示:
1 <= barcodes.length <= 10000
1 <= barcodes[i] <= 10000
数组的已有元素重新排列,保证相邻元素不相同。由于题目保证存在答案,直接用贪心的方法就可以实现。
先统计出每个元素的个数,按照个数排序分配。
按从最多次数的开始排,开始排的放在偶数下标位置。这样子保证出现很多次的一定是会分开的。然后将剩下的插入到奇数下标处。这样子因为是最多的先放,少的会因为是多的先放了,所以少的不会出现连着出现的情况。
第一步:统计出现的次数和对应的值。这里我们用了 pair 与vector 的结合使用。然后升序排序。
第二步:从出现次数多的先放,先把偶数下标放完,再从头放奇数下标。
class Solution {
public:
const int MAXN = 10000+10;
vector rearrangeBarcodes(vector& barcodes) {
vector ans(barcodes.size());
vector cnt(MAXN,0);
for(int i = 0;i < barcodes.size();i++)
{
cnt[barcodes[i]]++;
}
vector> numAndVal;
for(int i = 0;i < MAXN;i++)
{
if(cnt[i] > 0)
numAndVal.push_back(make_pair(cnt[i],i));
}
sort(numAndVal.begin(),numAndVal.end());
int i = 0;
int j = numAndVal.size()-1;
int k = 0;
while(k < 2)
{
for(i = k;i < barcodes.size();i += 2)
{
if(numAndVal[j].first == 0)
j--;
ans[i] = numAndVal[j].second;
--numAndVal[j].first;
}
++k;
}
return ans;
}
};