LeetCode第138场周赛(Weekly Contest 138)解题报告

这次是第一次参加周赛,在参加之前看了上一场的周赛题目,发现最多也就Medium 难度。

而这次的题目还是算简单的,在比赛时由于中途有事走了,时间内完成两题,后两题是晚上补完的。

题目有点绕,比如第一题,一开始我以为是一个求LIS的问题,然后最后发现其实很简单,哈哈哈。

第三题的示例有点问题,在写完代码之后运行出错,发现第四个示例出了问题。所以有时候代码还是要根据具体题目来进行微小修改的。



LeetCode第138场周赛地址:

https://leetcode-cn.com/contest/weekly-contest-138/


1. 高度检查器(Height Checker)

题目链接

https://leetcode-cn.com/contest/weekly-contest-138/problems/height-checker/

题意

学校在拍年度纪念照时,一般要求学生按照 非递减 的高度顺序排列。

请你返回至少有多少个学生没有站在正确位置数量。该人数指的是:能让所有学生以 非递减 高度排列的必要移动人数。

示例:

输入:[1,1,4,2,1,3]
输出:3
解释:
高度为 4、3 和最后一个 1 的学生,没有站在正确的位置。

提示:

  1. 1 <= heights.length <= 100
  2. 1 <= heights[i] <= 100

解题思路

其实很简单,就是本来应该是一个非递减数组,求有几个学生没有站到正确位置。

将数组排序,然后利用排序数组和原本的数组进行比较,如果该位置数值不相等,说明站错位了。

时间复杂度是 O(nlogn),不会超时。

前提知识点

vector 的使用,可以看我的另一篇博客:C++的vector用法

AC代码

class Solution {
public:
    int heightChecker(vector<int>& heights) {
        vector<int> 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;
    }
};

 


2. 爱生气的书店老板(Grumpy Bookstore Owner)

题目链接

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,所以就是返回第一个数组所有元素求和。

AC代码

class Solution {
public:
    int maxSatisfied(vector<int>& customers, vector<int>& 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;
    }
};

 


3.交换一次的先前排列(Previous Permutation With One Swap)

题目链接

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. 1 <= A.length <= 10000
  2. 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]<A[s]),多加个等号。所以有的时候,程序还是要为了完成出的题而进行适当修改的。

AC代码

class Solution {
public:
    vector<int> prevPermOpt1(vector<int>& 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]<A[s])
                e = i;
        }
        int temp = A[s];
        A[s] = A[e];
        A[e] = temp;
        return A;
    }
};

 


4.距离相等的条形码(Distant Barcodes)

题目链接

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. 1 <= barcodes.length <= 10000
  2. 1 <= barcodes[i] <= 10000

解题分析

数组的已有元素重新排列,保证相邻元素不相同。由于题目保证存在答案,直接用贪心的方法就可以实现。

先统计出每个元素的个数,按照个数排序分配。

按从最多次数的开始排,开始排的放在偶数下标位置。这样子保证出现很多次的一定是会分开的。然后将剩下的插入到奇数下标处。这样子因为是最多的先放,少的会因为是多的先放了,所以少的不会出现连着出现的情况。

第一步:统计出现的次数和对应的值。这里我们用了 pair 与vector 的结合使用。然后升序排序。

第二步:从出现次数多的先放,先把偶数下标放完,再从头放奇数下标。

AC代码

class Solution {
public:
    const int MAXN = 10000+10;
    vector<int> rearrangeBarcodes(vector<int>& barcodes) {
        vector<int> ans(barcodes.size());
        vector<int> cnt(MAXN,0);
        for(int i = 0;i < barcodes.size();i++)
        {
            cnt[barcodes[i]]++;
        }
        
        vector<pair<int, int>> 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;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值