双周赛第84场

双周赛84

​ 这次双周赛,其实我只做出来了第一题,下面这三道都是听了讲解才写出来。

2364 统计坏数对的数目

​ 这道题给出条件:如果 i < j 并且j - i != nums[j] - nums[i] ,那么我们称(i, j) 是一个坏数对。

​ 这里的式子两边都是两个字母,那这个规律直接用得话,去枚举需要n^2的时间复杂度,所以我们做一个变换,满足nums[j] - j != nums[i] - i(i,j)是一个坏数对。那么这个时候,式子的一边只有一个字母,复杂度就降下来了。那么这时候就明了, 我们可以使用map存nums[i] - i,然后求好数对的个数,用总的数对数相减就好。

typedef long long LL;
class Solution {
public:
    LL C(int n){  // 求组合数
        return n*(n-1ll)/2;
    }
    long long countBadPairs(vector<int>& nums) {
        int n = nums.size();
        LL res = C(n);
        unordered_map<int,int> m;
        for(int i = 0; i<n; i++) {
            m[nums[i]-i]++;
        }
        // 学到了一个迭代map的方法。
        for(auto&[k,v] : m) {
            res -= C(v);
        }
        return res;
    }   
};

这道题主要就是学到了对式子做变换降低复杂度的技巧.

2365 任务调度器II

​ 这道题其实是用贪心的思想写,一个任务,能做就做,越早做越好。

​ 我们记录任务上一次是在哪天做的,然后判断需要等还是直接做。

typedef long long LL;
class Solution {
public:
    long long taskSchedulerII(vector<int>& tasks, int space) {
        LL res =0;  // 最后返回的天数
        unordered_map<LL,LL> m;
        int n = tasks.size();
        for(auto a : tasks) {
            res = max(res+1,m[a]);  // 满足两个条件,比前一天+1,与上一次任务间隔space天
            m[a]  = res + space+1; 
        }
        return res;
    }
};

2366 将数组排序的最少替换次数

​ 给你一个下表从 0 开始的整数数组 nums 。每次操作中,你可以将数组中任何一个元素替换为 任意两个 和为该元素的数字。

比方说,nums = [5,6,7] 。一次操作中,我们可以将 nums[1] 替换成 2 和 4 ,将 nums 转变成 [5,2,4,7] 。
请你执行上述操作,将数组变成元素按 非递减 顺序排列的数组,并返回所需的最少操作次数。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/minimum-replacements-to-sort-the-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

这道题也是贪心的一个做法,现在a和b挨着,b在a后面,那么我们就要考虑让a分割为比b小的数,并且分割出来的数还要非降序排列。我们假设分割成了k个数,a1 + a2 + a3 +... + ak = a,这时候要想同两点:

  1. a分割出来的数越大越好,这样的话,a1前面的数就好划分了,就可以划分更少次

  2. k越小越好,让分割次数变少嘛。

    这两点统一起来,我们就得到个什么呢,那就是让a均分为k个,然后,怎么确定最小的k呢,我们用 a/b向上取整,就能确定k(分割出出来的数大,k还小).这样分解出来的数都比b小,如果a/k 有余数为r,那么就在后r个数每个数加1就行了。

下面的代码用到了一个技巧:求a/b向上取整,等于 a+b-1 /b 向下取整.

typedef long long LL;

class Solution {
public:
    long long minimumReplacement(vector<int>& nums) {
        LL res = 0;
        int n = nums.size(); 
        for(int i = n-2,last = nums.back();i>=0;i--) { // 最后一个不用动 ,last表示上一次处理找到的最小的数。
            if(nums[i]<=last) {   // 当这个数比上次的小时,不用分割,改下last就行了。
                last = nums[i];  
            }else {
                int k = (nums[i] + last - 1)/last;  //a/b向上取整,等于 a+b-1 /b 向下取整
                res += k-1;  // 分出来k份,等于就是分了k-1次。
                last = nums[i]/k;
            }
        }
        return res;
    }
};
posted @ 2022-08-12 14:20  博客是个啥?  阅读(13)  评论(0编辑  收藏  举报