Leetcode - 133双周赛

目录

[一,3190. 使所有元素都可以被 3 整除的最少操作数](#一,3190. 使所有元素都可以被 3 整除的最少操作数)

[二,3191. 使二进制数组全部等于 1 的最少操作次数 I](#二,3191. 使二进制数组全部等于 1 的最少操作次数 I)

[三,3192. 使二进制数组全部等于 1 的最少操作次数 II](#三,3192. 使二进制数组全部等于 1 的最少操作次数 II)

[四,3193. 统计逆序对的数目](#四,3193. 统计逆序对的数目)


一,3190. 使所有元素都可以被 3 整除的最少操作数

本题可以直接模拟,如果使用减法操作,那么需要操作 x % 3 次;如果使用加法操作,那么需要操作 3 - x % 3 次。问最少的操作次数,直接取两者的最小值就行。

代码如下:

复制代码
class Solution {
    public int minimumOperations(int[] nums) {
        int ans = 0;
        for(int x : nums){
            ans += Math.min(Math.abs(3-x%3), x%3);
        }
        return ans;
    }
}

二,3191. 使二进制数组全部等于 1 的最少操作次数 I

本题直接从左往右遍历,注 i < nums.length-2 :

  • 遇到0,将nums[i],nums[i+1],nums[i+2] 反转(即 ^1),ans++
  • 遇到1,什么都不做
  • 循环结束判断后两个数是否全为1,如果是,返回ans;否则返回-1

代码如下:

复制代码
class Solution {
    public int minOperations(int[] nums) {
        int ans = 0;
        int i = 0;
        for(; i<nums.length-2; i++){
            if(nums[i]==0){
                nums[i] ^= 1;
                nums[i+1] ^= 1;
                nums[i+2] ^= 1;
                ans++;
            }
        }
        return nums[i]==1 && nums[i+1]==1 ? ans : -1;
    }
}

三,3192. 使二进制数组全部等于 1 的最少操作次数 II

本题也可以采用上述做法,代码如下:

复制代码
class Solution {
    public int minOperations(int[] nums) {
        int n = nums.length;
        int ans = 0;
        for(int i=0; i<n; i++){
            if(nums[i] == 0){
                for(int j=i; j<n; j++)
                    nums[j] ^= 1;
                ans++;
            }
        }
        return ans;
    }
}

但是该做法是O(n^2)的时间复杂度,会超时,那么上述做法还有哪里可以优化?可以发现如果一个数执行 ^1操作偶数次,它就会变回原来的值,所以我们可以统计后续元素需要执行反转操作的次数cnt,在枚举到x时,如果cnt为奇数,x ^=1,再判断 x 是否为 0,如果为0,cnt++。依次类推,最终得到的cnt就是答案。

代码如下:

复制代码
class Solution {
    public int minOperations(int[] nums) {
        int ans = 0;
        for(int i=0; i<nums.length; i++){
            if(ans%2==1)
                nums[i] ^= 1;
            if(nums[i] == 0){
                ans++;
            }
        }
        return ans;
    }
}

四,3193. 统计逆序对的数目

本题可以从后先前考虑,假设有3个数,构造逆序对为2的排序:

  • 如果最后一个数是2,那么该数与[0,i-1]能组成0个逆序对,就需要[0,i-1]有2个逆序对
  • 如果最后一个数是1,那么该数与[0,i-1]能组成1个逆序对,就需要[0,i-1]有1个逆序对
  • 如果最后一个数是0,那么该数与[0,i-1]能组成2个逆序对,就需要[0,i-1]有0个逆序对

依次类推,上述问题就化成了与原问题相同的子问题。可以定义dfs(i,j):前 i 个数有 j 个逆序对时的排序个数。

  • 没有requirements束缚,假设 k 为 perm[i] 小于[0,i-1]元素的个数,即 perm[i] 能产生 k 个逆序对,那么问题就转换成了前 i-1个数有 j - k 个逆序对的排序个数。(注:k <= Math.min(i,j))
  • 有requirements束缚,该问题就只能转换成前 i-1个数有 req[i-1] 个逆序对的排序个数。(注:req[i-1] <= j && req[i-1] >= j - i,这两个条件就表示req[i-1]的范围必须在[ j - i,j],可以这样理解,当前perm[i]能与前i-1个数组成[0,i]个逆序对,那么前i-1个数需要有[j - i,j]个逆序对)

代码如下:

复制代码
class Solution {
    public int numberOfPermutations(int n, int[][] requirements) {
        int[] req = new int[n];
        Arrays.fill(req, -1);
        req[0] = 0;
        for(int[] x : requirements){
            req[x[0]] = x[1];
        }
        if(req[0]>0) return 0; 
        for(int[] r : memo)
            Arrays.fill(r, -1);
        return dfs(n-1, req[n-1], req);
    }
    int[][] memo = new int[301][401];
    int dfs(int i, int j, int[] req){
        if(i == 0) return 1;
        if(memo[i][j] != -1) return memo[i][j];
        int res = 0;
        int cnt = req[i-1];
        if(cnt >= 0){
            if(cnt <= j && cnt >= j-i)
                res = dfs(i-1, cnt, req);
        }else{
            for(int k=0; k<=Math.min(i, j); k++){
                res = (res + dfs(i-1, j-k, req))%1_000_000_007;
            }
        }
        return memo[i][j] = res;
    }
}
相关推荐
Xの哲學33 分钟前
Perf使用详解
linux·网络·网络协议·算法·架构
奶黄小甜包38 分钟前
C语言零基础第18讲:自定义类型—结构体
c语言·数据结构·笔记·学习
想不明白的过度思考者43 分钟前
数据结构(排序篇)——七大排序算法奇幻之旅:从扑克牌到百亿数据的魔法整理术
数据结构·算法·排序算法
小七rrrrr1 小时前
动态规划法 - 53. 最大子数组和
java·算法·动态规划
code小毛孩1 小时前
leetcodehot100 矩阵置零
算法
一支闲人1 小时前
C语言相关简单数据结构:双向链表
c语言·数据结构·链表·基础知识·适用于新手小白
何妨重温wdys1 小时前
矩阵链相乘的最少乘法次数(动态规划解法)
c++·算法·矩阵·动态规划
姜不吃葱1 小时前
【力扣热题100】双指针—— 接雨水
数据结构·算法·leetcode·力扣热题100
PineappleCoder1 小时前
大小写 + 标点全搞定!JS 如何精准统计单词频率?
前端·javascript·算法
zzx_blog1 小时前
简单易懂的leetcode 100题-第三篇 移动0,颜色分类,数组中的第K个最大元素
leetcode·面试