LeetCode 第29场夜喵双周赛 题解

今天做项目迟了半个小时才开始,还好这次比赛难度比较简单,还是把四题秒了

文章目录

  • a.去掉最低工资和最高工资后的工资平均值
    • a.题目
    • a.分析
    • a.参考代码
  • b.n 的第 k 个因子
    • b.题目
    • b.分析
    • b.参考代码
  • c.删掉一个元素以后全为 1 的最长子数组
    • c.题目
    • c.分析
    • c.参考代码
  • d.并行课程 II
    • d.题目
    • d.分析
    • d.参考代码

a.去掉最低工资和最高工资后的工资平均值

a.题目

给你一个整数数组 salary ,数组里每个数都是 唯一 的,其中 salary[i] 是第 i 个员工的工资。
请你返回去掉最低工资和最高工资以后,剩下员工工资的平均值。

示例 1

输入:salary = [4000,3000,1000,2000]
输出:2500.00000
解释:最低工资和最高工资分别是 1000 和 4000 。
去掉最低工资和最高工资以后的平均工资是 (2000+3000)/2= 2500

示例 2

输入:salary = [1000,2000,3000]
输出:2000.00000
解释:最低工资和最高工资分别是 1000 和 3000 。
去掉最低工资和最高工资以后的平均工资是 (2000)/1= 2000

示例 3

输入:salary = [6000,5000,4000,3000,2000,1000]
输出:3500.00000

示例 4

输入:salary = [8000,9000,2000,3000,6000,1000]
输出:4750.00000

提示

  • 3 <= salary.length <= 100
  • 10^3 <= salary[i] <= 10^6
  • salary[i] 是唯一的。
  • 与真实值误差在 10^-5 以内的结果都将视为正确答案。

a.分析

小学数学题
按照题意模拟即可

  • 找到最大和最小
  • 然后求和
  • 在和中减去最大最小
  • 求平均值

总的时间复杂度是O(n)

a.参考代码

class Solution {
public:
    double average(vector<int>& s) {
        double Max=*max_element(s.begin(),s.end());	//用的库函数 快点
        double Min=*min_element(s.begin(),s.end());
        double ans=0.0;
        for(auto i:s)
            ans+=i;
        ans-=Max+Min;
        ans/=s.size()-2;	//记得少了两个元素
        return ans;
    }
};

b.n 的第 k 个因子

b.题目

给你两个正整数 nk
如果正整数 i 满足 n % i == 0 ,那么我们就说正整数 i 是整数 n 的因子。
考虑整数 n 的所有因子,将它们 升序排列 。请你返回第 k 个因子。如果 n 的因子数少于 k ,请你返回 -1 。

示例 1

输入:n = 12, k = 3
输出:3
解释:因子列表包括 [1, 2, 3, 4, 6, 12],第 3 个因子是 3 。

示例 2

输入:n = 7, k = 2
输出:7
解释:因子列表包括 [1, 7] ,第 2 个因子是 7 。

示例 3

输入:n = 4, k = 4
输出:-1
解释:因子列表包括 [1, 2, 4] ,只有 3 个因子,所以我们应该返回 -1 。

示例 4

输入:n = 1, k = 1
输出:1
解释:因子列表包括 [1] ,第 1 个因子为 1 。

示例 5

输入:n = 1000, k = 3
输出:4
解释:因子列表包括 [1, 2, 4, 5, 8, 10, 20, 25, 40, 50, 100, 125, 200, 250, 500, 1000] 。

提示

  • 1 <= k <= n <= 1000

b.分析

这道题放在第一题才对的
数据范围太小了 不过纯模拟也才 O(n)

  • 把因数用试除法全部找出来放在一个数组里
  • 返回数组第k个因子
  • 判断下数组的大小能不能返回

b.参考代码

class Solution {
public:
    int kthFactor(int n, int k) {
        vector<int> x;
        for(int i=1;i<=n;i++)
            if(!(n%i))x.push_back(i);	//试除法求因子
        if(x.size()<k)return -1;	//没有第k个
        return x[k-1];
    }
};

c.删掉一个元素以后全为 1 的最长子数组

c.题目

给你一个二进制数组 nums ,你需要从中删掉一个元素。
请你在删掉元素的结果数组中,返回最长的且只包含 1 的非空子数组的长度。
如果不存在这样的子数组,请返回 0

示例 1

输入:nums = [1,1,0,1]
输出:3
解释:删掉位置 2 的数后,[1,1,1] 包含 3 个 1 。

示例 2

输入:nums = [0,1,1,1,0,1,1,0,1]
输出:5
解释:删掉位置 4 的数字后,[0,1,1,1,1,1,0,1] 的最长全 1 子数组为 [1,1,1,1,1] 。

示例 3

输入:nums = [1,1,1]
输出:2
解释:你必须要删除一个元素。

示例 4

输入:nums = [1,1,0,0,1,1,1,0,1]
输出:4

示例 5

输入:nums = [0,0,0]
输出:0

提示

  • 1 <= nums.length <= 10^5
  • nums[i] 要么是 0 要么是 1 。

c.分析

看了下数据范围 应该是要用O(n)或者O(nlogn)的做法的
那么先想下一次遍历的做法

因为只能删除一个元素 所以有0的话肯定是删除0的 连续的1不可能被拆
删一个0后只会影响到前后的两个1的子串
因为其他都被分割开来了

考虑像这样的: 111 00 11 00 111 0 11 0 111

我们可以看到 上面被分成了5个1的子串 且只有间隔一个0的相邻我们才能把它合并

因此我们只需要模拟以下几点:

  • 把每一组的1的长度算出来
  • 每一组的1都去看下前面相邻的组能不能合并 即相差一个0
  • 如果能和并 则尝试把两组长度加起来 然后更新答案
  • 全部为1的话 答案为长度-1

最终遍历完答案就出来了 总的时间复杂度是线性一次遍历即可 O(n)

c.参考代码

class Solution {
public:
    int longestSubarray(vector<int>& nums) {
        int ans=0;
        int now=0,last=0,add=0;		//线性写法 不用记录每组的长度 now为当前长度 last为上一组长度 add表示是否添加
		//在开头判断是否添加 因为此时now不知道 在结束把add加上更新答案
        for(int i=0;i<nums.size();i++)
            if(nums[i]){	//是1
                if(!now&&last&&nums[i-2])add=last;	//刚开始的1 去判断下前面的能不能合并
                now++;
            }
            else {	//是0 更新下答案
                ans=max(ans,now+add);
                last=now;
                now=0;
                add=0;
            }
        ans=max(ans,now+add);
        if(ans==nums.size())return ans-1;	//注意必须要删除一个元素
        return ans;
    }
};

d.并行课程 II

d.题目

给你一个整数 n 表示某所大学里课程的数目,编号为 1n ,数组 dependencies 中, dependencies[i] = [xi, yi] 表示一个先修课的关系,也就是课程 xi 必须在课程 yi 之前上。同时你还有一个整数 k
在一个学期中,你 最多 可以同时上 k 门课,前提是这些课的先修课在之前的学期里已经上过了。
请你返回上完所有课最少需要多少个学期。题目保证一定存在一种上完所有课的方式。

示例 1

输入:n = 4, dependencies = [[2,1],[3,1],[1,4]], k = 2
输出:3
解释:上图展示了题目输入的图。在第一个学期中,我们可以上课程 2 和课程 3 。然后第二个学期上课程 1 ,第三个学期上课程 4 。

示例 2

输入:n = 5, dependencies = [[2,1],[3,1],[4,1],[1,5]], k = 2
输出:4
解释:上图展示了题目输入的图。一个最优方案是:第一学期上课程 2 和 3,第二学期上课程 4 ,第三学期上课程 1 ,第四学期上课程 5 。

示例 3
输入:n = 11, dependencies = [], k = 2
输出:6

提示

  • 1 <= n <= 15
  • 1 <= k <= n
  • 0 <= dependencies.length <= n * (n-1) / 2
  • dependencies[i].length == 2
  • 1 <= xi, yi <= n
  • xi != yi
  • 所有先修关系都是不同的,也就是说 dependencies[i] != dependencies[j] 。
  • 题目输入的图是个有向无环图。

d.分析

其实就是一个拓扑序的问题 不过是要带权重的问题
即不是每个拓扑序都是最优的

拓扑序复习
用BFS,每次把0入度的点加入进队列中

这题可以看作是答案就是一个层数的问题
可以看作BFS每层最多k个节点

有以下两种情况:

  • 这层的节点数小于k

那么就全部上完就行了

  • 这层的节点数大于k

这里需要注意了 这里选谁先进行遍历是有先后优劣之分的
假如说k=2,你有1,2,3节点可以选,但是3后面有一个4需要3为先修条件
假如你选择1,2 那么下一次你只能3 最后4
但是如果你选了1,3或者2,3的话 下一次是可以4和另一个一起修完的

这里直接给出以上说法的结论:
节点后面有越多节点的先修了,反证:如果不这样做的话,可能会有:这个节点会解锁k个节点,然后不选这个节点排不满k个,那么时间就浪费了

所以这里每层需要排序下
其他的和BFS求层数一样,就不细说了

总的时间复杂度为遍历整个图的边O(m)这个复杂度少得可怜呢

d.参考代码

vector<vector<int>> G;
class Solution {
public:
    int minNumberOfSemesters(int n, vector<vector<int>>& d, int k) {
        G.clear();
        G.resize(n+1);	//邻接表
        vector<int> in(n+1);	//入度表
        for(auto i:d)	//处理图
        {
            in[i[1]]++;
            G[i[0]].push_back(i[1]);
        }
        priority_queue<int,vector<int>,cmp<int>> q;		//优先队列 因为每层有个数有限
        for(int i=1;i<=n;i++)if(!in[i])q.push(i);
        int ans=0;
        while(q.size())	//正常的BFS拓扑内容
        {
            int t=min(k,(int)q.size());		//每层数量最大k
            ans++;
            vector<int> tmp;	//因为当前层的东西不可能会因为新解锁的被改变的,需要保持队列性质
            while(t--)
            {
                int now=q.top();
                q.pop();
                for(auto i:G[now]){
                    in[i]--;
                    if(!in[i])tmp.push_back(i);
                }
            }
            for(auto i:tmp)q.push(i);
        }
        return ans;
    }
    template <typename T>	//优先队列要这样写
    struct cmp{
        bool operator()(T a,T b)
        {
            return G[a].size()<G[b].size();	//重点 根据可解锁节点的数量排序
        }
    };
};

你可能感兴趣的:(leetcode,周赛)