刷爆 LeetCode 双周赛 100,单方面宣布第一题最难

2023-11-03

上周末是 LeetCode 第 100 场双周赛,你参加了吗?这场周赛整体没有 Hard 题,但是也没有 Easy 题。第一题国服前百名里超过一半人 wa,很少见。


小彭的技术交流群 02 群来了,公众号回复 “加群” 加入我们~



周赛概览

  • 2591. 将钱分给最多的儿童(Easy)

  • 题解一:模拟 $O(1)$

  • 题解二:完全背包 $O(children·money^2)$

  • 2592. 最大化数组的伟大值(Medium)

  • 题解一:贪心 / 田忌赛马 $O(nlgn)$

  • 题解二:最大重复计数 $O(n)$

  • 2593. 标记所有元素后数组的分数(Medium)

  • 题解一:排序 O$(nlgn)$

  • 题解二:按照严格递减字段分组 $O(n)$

  • 2594. 修车的最少时间(Medium)

  • 题解一:二分查找 $O(n + U·log(mc^2))$

  • 题解二:二分查找 + 计数优化 $O(n·log(mc^2))$


2591. 将钱分给最多的儿童(Easy)

题目地址

https://leetcode.cn/problems/distribute-money-to-maximum-children/description/

题目描述

给你一个整数 money ,表示你总共有的钱数(单位为美元)和另一个整数 children ,表示你要将钱分配给多少个儿童。

你需要按照如下规则分配:

  • 所有的钱都必须被分配。

  • 每个儿童至少获得 1 美元。

  • 没有人获得 4 美元。

请你按照上述规则分配金钱,并返回 最多 有多少个儿童获得 恰好 **8 美元。如果没有任何分配方案,返回 -1 。

题解一(模拟)

这道题搞数字迷信?发发发 888?

简单模拟题,但是错误率很高,提交通过率仅 19%。

classSolution{
    fundistMoney(money: Int, children: Int): Int {
        var left = money
        // 每人至少分配 1 元
        left -= children
        // 违反规则 2if (left < 0) return -1// 1、完美:正好所有人可以分配 8 元if (left == children * 7) return children
        // 2、溢出:所有人可以分配 8 元有结余,需要选择 1 个人分配结余的金额if (left > children * 7) return children - 1// 3、不足:尽可能分配 8 元var sum = left / 7// 结余金额
        left -= sum * 7// 如果结余 3 元,并且剩下 1 人分配了 1 元,需要破坏一个 8 元避免出现分配 4 美元if (left == 3 && children - sum == 1) return sum - 1return sum
    }
}

复杂度分析:

  • 时间复杂度:$O(1)$

  • 空间复杂度:$O(1)$

题解二(完全背包问题)

竞赛中脑海闪现过背包问题的思路,但第一题暴力才是王道,赛后验证可行。

  • 包裹:最多有 children 人;

  • 物品:每个金币价值为 1 且不可分割,最多物品数为 money 个;

  • 目标:包裹价值恰好为 8 的最大个数;

  • 限制条件:不允许包裹价值为 4,每个包裹至少装 1 枚金币。

令 dp[i][j] 表示分配到 i 个人为止,且分配总金额为 j 元时的最大价值,则有:

  • 递推关系:

$$

dp[i][j]=\sum_{k=1}^{j,k!=4} dp[i-1][j-k] + I(k==8)

$$

  • 初始状态 dp[0][0] = 0

  • 终止状态 dp[children][money]

classSolution{
    fundistMoney(money: Int, children: Int): Int {
        var left = money
        // 每人至少分配 1 元
        left -= children
        // 违反规则 2if (left < 0) return -1val dp = Array(children + 1) { IntArray(left + 1) { -1 } }
        dp[0][0] = 0// i:枚举包裹for (i in1..children) {
            // j:枚举金额for (j in0..left) {
                // k:枚举选项for (k in0..j) {
                    // 不允许选择 3if (k == 3) continue// 子状态违反规则if (-1 == dp[i - 1][j - k]) continue// 子状态 + 当前包裹状态val cnt = dp[i - 1][j - k] + if (k == 7) 1else0
                    dp[i][j] = Math.max(dp[i][j], cnt)
                }
            }
        }
        return dp[children][left]
    }
}

滚动数组优化:

classSolution{
    fundistMoney(money: Int, children: Int): Int {
        var left = money
        // 每人至少分配 1 元
        left -= children
        // 违反规则 2if (left < 0) return -1val dp = IntArray(left + 1) { -1 }
        dp[0] = 0// i:枚举包裹for (i in1..children) {
            // j:枚举金额for (j in left downTo 0) {
                // k:枚举选项for (k in0..j) {
                    // 不允许选择 3if (k == 3) continue// 子状态违反规则if (-1 == dp[j - k]) continue// 子状态 + 当前包裹状态val cnt = dp[j - k] + if (k == 7) 1else0
                    dp[j] = Math.max(dp[j], cnt)
                }
            }
        }
        return dp[left]
    }

复杂度分析:

  • 时间复杂度:$O(children·money^2)$

  • 空间复杂度:$O(money)$

近期周赛背包问题:


2592. 最大化数组的伟大值(Medium)

题目地址

https://leetcode.cn/problems/maximize-greatness-of-an-array/

题目描述

给你一个下标从 0 开始的整数数组 nums 。你需要将 nums 重新排列成一个新的数组 perm 。

定义 nums 的 伟大值 为满足 0 <= i < nums.length 且 perm[i] > nums[i] 的下标数目。

请你返回重新排列 nums 后的 最大 伟大值。

题解一(贪心 / 田忌赛马)

贪心思路:田忌赛马,以下赛马策略最优:

  • 田忌的中等马对齐威王的下等马,田忌胜;

  • 田忌的上等马对齐威王的中等马,田忌胜;

  • 田忌的下等马对齐威王的下等马,齐威王胜。

回到本题,考虑一组贡献伟大值的配对 $(p, q)$,其中 $p < q$。由于越小的值越匹配到更大值,为了让结果最优,应该让 p 尽可能小,即优先匹配 nums 数组的较小值。那么 $q$ 如何选择呢?有 2 种策略:

  • 策略 1 - 优先匹配最大值:无法得到最优解,因为会消耗了较大的 q 值,可能导致部分 p 值无法匹配(如果田忌用上等马对齐威王的下等马,最终将是齐威王生出);

  • 策略 2- 优先匹配最接近的更大值:最优解,即田忌赛马策略,以 [1,1,1,2,3,3,5] 为例:

  • 初始状态 i = 0,j = 0;

  • i = 0,j = 0,无法贡献伟大值,j 自增 1(寻找最接近的更大值);

  • i = 0,j = 1, 无法贡献伟大值,j 自增 1;

  • i = 0,j = 2, 无法贡献伟大值,j 自增 1;

  • i = 0,j = 3, 贡献伟大值,j 自增 1,i 自增 1;

  • i = 1,j = 4, 贡献伟大值,j 自增 1,i 自增 1;

  • i = 2,j = 5, 贡献伟大值,j 自增 1,i 自增 1;

  • i = 3,j = 6, 贡献伟大值,j 自增 1,i 自增 1;

  • 退出循环,i = 4;正好等于伟大值 4。

classSolution{
    funmaximizeGreatness(nums: IntArray): Int {
        nums.sort()
        // i:参与匹配的指针var i = 0for (num in nums) {
            // 贡献伟大值if (num > nums[i]) i++
        }
        return i
    }
}

复杂度分析:

  • 时间复杂度:$O(nlgn + n)$ 排序 + 线性遍历,其中 $n$ 是 $nums$ 数组长度;

  • 空间复杂度:$O(lgn)$ 排序递归栈空间。

题解二(最大重复计数)

竞赛中从测试用例中观察到题解与最大重复数存在关系,例如:

  • 用例 [1,1,1,2,3,3,5]:最大重复数为 3,一个最优方案为 [2,3,3,5,x,x,x],最大伟大值为 7 - 3 = 4,其中 7 是数组长度;

  • 用例 [1,2,2,2,2,3,5]:最大重复数为 4,一个最优方案为 [2,3,5,x,x,x,x],最大伟大值为 7 - 4 = 3,其中 7 是数组长度;

  • 用例 [1,1,2,2,2,2,3,3,5],最大重复数为 4,一个最优方案为 [2,2,3,3,5,x,x,x,x],最大伟大值为 9 - 4 = 5,其中 9 是数组长度。

我们发现题目的瓶颈在于数字最大重复出现计数。最大伟大值正好等于 数组长度 - 最大重复计数。

如何证明?关键在于 i 指针和 j 指针的最大距离:

当 i 指针指向重复元素的首个元素时(例如下标为 0、2、6 的位置),j 指针必须移动到最接近的较大元素(例如下标为 2,6,8 的位置)。而 i 指针和 j 指针的最大错开距离取决于数组重复出现次数最多的元素,只要错开这个距离,无论数组后续部分有多长,都能够匹配上。

classSolution{
    funmaximizeGreatness(nums: IntArray): Int {
        var maxCnt = 0val cnts = HashMap<Int, Int>()
        for (num in nums) {
            cnts[num] = cnts.getOrDefault(num, 0) + 1
            maxCnt = Math.max(maxCnt, cnts[num]!!)
        }
        return nums.size - maxCnt
    }
}

复杂度分析:

  • 时间复杂度:$O(n)$ 其中 $n$ 是 $nums$ 数组的长度;

  • 空间复杂度:$O(n)$ 其中 $n$ 是 $cnts$ 散列表空间。


2593. 标记所有元素后数组的分数(Medium)

题目地址

https://leetcode.cn/problems/find-score-of-an-array-after-marking-all-elements/

题目描述

给你一个数组 nums ,它包含若干正整数。

一开始分数 score = 0 ,请你按照下面算法求出最后分数:

  • 从数组中选择最小且没有被标记的整数。如果有相等元素,选择下标最小的一个。

  • 将选中的整数加到 score 中。

  • 标记 被选中元素,如果有相邻元素,则同时标记 与它相邻的两个元素

  • 重复此过程直到数组中所有元素都被标记。

请你返回执行上述算法后最后的分数。

题解一(排序)

这道题犯了大忌,没有正确理解题意。一开始以为 “相邻的元素” 是指未标记的最相邻元素,花了很多时间思考如何快速找到左右未标记的数。其实题目没有这么复杂,就是标记数组上的相邻元素。

如此这道题只能算 Medium 偏 Easy 难度。

classSolution{
    funfindScore(nums: IntArray): Long {
        // 小顶堆(索引)val heap = PriorityQueue<Int>() { i1, i2 ->
            if (nums[i1] != nums[i2]) nums[i1] - nums[i2] else i1 - i2
        }.apply {
            for (index in nums.indices) {
                offer(index)
            }
        }
        var sum = 0Lwhile (!heap.isEmpty()) {
            val index = heap.poll()
            if (nums[index] == 0) continue// 标记
            sum += nums[index]
            nums[index] = 0// 标记相邻元素if (index > 0) nums[index - 1] = 0if (index < nums.size - 1) nums[index + 1] = 0
        }
        return sum
    }
}

复杂度分析:

  • 时间复杂度:$O(nlgn)$ 堆排序时间,其中 $n$ 是 $nums$ 数组长度;

  • 空间复杂度:$O(n)$ 堆空间。

题解二(按照严格递减字段分组)

思路参考:灵茶山艾府的题解

按照严格递减字段分组,在找到坡底后间隔累加 nums[i],nums[i - 2],nums[i - 4],并从 i + 2 开始继续寻找坡底。

classSolution{
    funfindScore(nums: IntArray): Long {
        val n = nums.size
        var sum = 0Lvar i = 0while (i < nums.size) {
            val i0 = i // 坡顶while (i + 1 < n && nums[i] > nums[i + 1]) i++ // 寻找坡底for (j in i downTo i0 step 2) { // 间隔累加
                sum += nums[j]
            }
            i += 2// i + 1 不能选
        }
        return sum
    }
}

复杂度分析:

  • 时间复杂度:$O(n)$ 其中 $n$ 是 $nums$ 数组的长度,每个元素最多访问 2 次;

  • 空间复杂度:$O(1)$ 只使用常数空间。


2594. 修车的最少时间(Medium)

题目地址

https://leetcode.cn/problems/minimum-time-to-repair-cars/

题目描述

给你一个整数数组 ranks ,表示一些机械工的 能力值 。ranksi 是第 i 位机械工的能力值。能力值为 r 的机械工可以在 r * n2 分钟内修好 n 辆车。

同时给你一个整数 cars ,表示总共需要修理的汽车数目。

请你返回修理所有汽车 最少 需要多少时间。

注意: 所有机械工可以同时修理汽车。

题解(二分查找)

我们发现问题在时间 t 上存在单调性:

  • 假设可以在 t 时间内修完所有车,那么大于 t 的时间都能修完;

  • 如果不能在 t 时间内修完所有车,那么小于 t 的时间都无法修完。

因此,我们可以用二分查找寻找 “可以修完的最小时间”:

  • 二分的下界:1;

  • 二分的上界:将所有的车交给能力值排序最高的工人,因为他的效率最高。

classSolution{
    funrepairCars(ranks: IntArray, cars: Int): Long {
        // 寻找能力值排序最高的工人var minRank = Integer.MAX_VALUE
        for (rank in ranks) {
            minRank = Math.min(minRank, rank)
        }
        var left = 1Lvar right = 1L * minRank * cars * cars
        // 二分查找while (left < right) {
            val mid = (left + right) ushr 1if (check(ranks, cars, mid)) {
                right = mid
            } else {
                left = mid + 1
            }
        }
        return left
    }

    // return 能否在 t 时间内修完所有车privatefuncheck(ranks: IntArray, cars: Int, t: Long): Boolean {
        // 计算并行修车 t 时间能修完的车(由于 t 的上界较大,carSum 会溢出 Int)var carSum = 0Lfor (rank in ranks) {
            carSum += Math.sqrt(1.0 * t / rank).toLong()
        }
        return carSum >= cars
    }
}

复杂度分析:

  • 时间复杂度:$O(n·log(mc^2))$ 其中 $n$ 是 $ranks$ 数组长度,$m$ 是 $ranks$ 数组的最小值,$c$ 是车辆数量,二分的次数是 $O(log(mc^2))$,每次 $check$ 操作花费 $O(n)$ 时间;

  • 空间复杂度:$O(1)$ 仅使用常量级别空间。

题解二(二分查找 + 计数优化)

我们发现 $ranks$ 的取值范围很小,所有可以用计数优化每次 $check$ 操作的时间复杂度:

classSolution{
    funrepairCars(ranks: IntArray, cars: Int): Long {
        // 寻找能力值排序最高的工人val cnts = IntArray(101)
        var minRank = Integer.MAX_VALUE
        for (rank in ranks) {
            minRank = Math.min(minRank, rank)
            cnts[rank]++
        }
        var left = 1Lvar right = 1L * minRank * cars * cars
        // 二分查找while (left < right) {
            val mid = (left + right) ushr 1if (check(ranks, cars, cnts, minRank, mid)) {
                right = mid
            } else {
                left = mid + 1
            }
        }
        return left
    }

    // return 能否在 t 时间内修完所有车privatefuncheck(ranks: IntArray, cars: Int, cnts: IntArray, minRank: Int, t: Long): Boolean {
        // 计算并行修车 t 时间能修完的车(由于 t 的上界较大,carSum 会溢出 Int)var carSum = 0Lfor (rank in minRank..100) {
            if (cnts[rank] == 0) continue
            carSum += cnts[rank] * Math.sqrt(1.0 * t / rank).toLong()
        }
        return carSum >= cars
    }
}

复杂度分析:

  • 时间复杂度:$O(n + U·log(mc^2))$ 其中 $n$ 是 $ranks$ 数组长度,$m$ 是 $ranks$ 数组的最小值,$U$ 是 $ranks$ 数组的取值范围,$c$ 是车辆数量,二分的次数是 $O(log(mc^2))$,每次 $check$ 操作花费 $O(U)$ 时间;

  • 空间复杂度:$O(U)$ $cnts$ 计数数组空间。

近期周赛二分查找题目:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

刷爆 LeetCode 双周赛 100,单方面宣布第一题最难 的相关文章

  • Percona-mysql server 5.5升级5.6

    http blog csdn net lqx0405 article details 50162557 系统环境 操作系统 CentOS 6 5 64 MySQL Percona server 5 5 5 6 一 升级的目的 为什么MySQ

随机推荐

  • Qt学习:Qt优雅地结束线程

    一 Qt线程 如果一个线程运行完成 就会结束 可很多情况并非这么简单 由于某种特殊原因 当线程还未执行完时 我们就想中止它 不恰当的中止往往会引起一些未知错误 比如 当关闭主界面的时候 很有可能次线程正在运行 这时 就会出现如下提示 QTh
  • noip2007 奖学金 (排序)

    A1159 奖学金 时间限制 1 0s 内存限制 256 0MB 总提交次数 797 AC次数 339 平均分 60 95 将本题分享到 查看未格式化的试题 提交 试题讨论 试题来源 NOIP2007 普及组 问题描述 某小学最近得到了一笔
  • 800-C++ throw(抛出异常)详解

    C throw 抛出异常 详解 抛出 Throw gt 检测 Try gt 捕获 Catch 异常必须显式地抛出 才能被检测和捕获到 如果没有显式的抛出 即使有异常也检测不到 在 C 中 我们使用 throw 关键字来显式地抛出异常 它的用
  • Office Online Server预览不了文件 TLS

    最近Office Online Server预览不了文件 服务器上报 从远程终点接收到一个严重警告 TLS 协议所定义的严重警告代码为 70 错误 经过排查发现TLS 1 1 和 TLS 1 2没有开启 将其开启后文档就能正常访问了 开启T
  • 如何利用R语言怎样处理百分数

    楼主在工作时 遇到一个问题 网上析取的资料中有很多百分数 但是R读取的时候把它默认为是因子类型了 用as numeric 函数也没有用 经过查找资料发现几个将百分数化成小数的小技巧 和大家分享一下 其基本思想就是把百分数按照字符处理 首先将
  • 基于亚博K210开发板——LED(RGB)点灯

    文章目录 开发板 实验目的 实验准备 查看原理图 软件对应SDK GPIO配置函数 什么是 FPIOA 呢 实验代码 LED RGB驱动 主程序控制 实验结果 开发板 实验目的 实现开发板上LED0 LED1以及RGB灯的点亮 实验准备 查
  • 用ISO C++实现自己的信号槽(Qt另类学习)

    有网友抱怨 哪个大牛能帮帮我 讲解一下信号槽机制的底层实现 不要那种源码的解析 只要清楚的讲讲是怎么发送信号 怎么去选择相应的槽 再做出反应 也就是类似于一个信号槽的相应流程 求解啊 看了源码 真的是一头雾水 撞墙的心都有了 本文使用 IS
  • 探索Vue组件通信的秘密:打破隔阂,实现数据共享

    一 Vue组件通信 每个组件都有自己的数据 提供在data中 每个组件的数据是独立的 组件数据无法互相直接访问 合理的 但是如果需要跨组件访问数据 就需要用到组件通信 要是有一万个商品 就要写一万个吗 函数调用 看起来调用时用一个函数 执行
  • js new Promise的基本用法

    function easyShare config return new Promise resolve reject gt try if config true console log 11 config setTimeout gt re
  • 2021秋招复习——CSS

    目录 文章目录 选择器 float布局 position定位 flex布局 水平垂直居中 水平居中 行内元素 块级元素 垂直居中 行内元素 块级元素 BFC 盒模型 CSS3动画 回流 重排 和重绘 响应式布局 选择器 选择器主要包括 选择
  • matlab求解正负因子目标规划,matlab学习系列27多目标规划.docx

    matlab学习系列27多目标规划 docx 27多目标规划一 线性规划的局限性1线性规划要求所求解问题必须满足全部的约束 而实际问题中并非所有约束都需要严格的满足 2线性规划只能处理单目标的优化问题 从而对一些次目标只能转化为约束处理 而
  • AngularJS 截取字符串

    In HTML Template Binding 在HTML的模板绑定中 limitTo expression limitTo limit begin In JavaScript filter limitTo input limit beg
  • 计算机开机键盘屏幕无反应,电脑开机后键盘显示器无反应怎么解决

    电脑开机后主机灯正常 有风扇和机器声音 但是键盘显示器都没有反应 这是怎么回事呢 电脑开机后键盘显示器无反应怎么解决呢 下面学习啦小编就为大家带来了解决电脑开机后键盘显示器无反应的方法 电脑开机后键盘显示器无反应解决方法一 开机状态下把鼠标
  • 机器学习(五):高斯朴素贝叶斯(基础篇)

    机器学习 五 高斯朴素贝叶斯 基础篇 在高斯朴素贝叶斯中 每个特征都是连续的 并且都呈高斯分布 高斯分布又称为正态分布 图画出来以后像一个倒挂的钟 以均值为轴对称 如下图所示 GaussianNB 实现了运用于分类的高斯朴素贝叶斯算法 特征
  • SQLyog出现错误代码1045

    直接修改mysql的密码即可
  • Elasticsearch 常见的 8 种错误及最佳实践

    Elasticsearch 社区有大量关于 Elasticsearch 错误和异常的问题 深挖这些错误背后的原因 把常见的错误积累为自己的实战经验甚至是工具 不仅可以节省我们的开发和运维时间 而且可以帮助确保 Elasticsearch 集
  • matlab批量读入dat数据,并将dat数据转换为tiff格式

    将dat数据 序号1 1500 读入matlab 并将其转换为 png格式 代码参考如下 clear close all num 1500 待读入的dat数量 addpath K 科目2 2 train dat dat 文件夹 cd K 科
  • Nginx 使用---拒绝指定IP访问

    一 问题描述 服务器可能会受到攻击者的恶意访问 攻击者IP会不断的猜测路径 上传文件 木马 或者进行短信消耗 或者破解密码 等等行为 我们要做的是 对这些恶意的访问IP进行拦截 二 Nginx的日志格式 因为首先一定是要查看日志的 所以首先
  • Oracle入门笔记(六)——多表查询

    多表查询 1 多表查询概览 2 基础多表查询 3 SQL99标准的外连接 4 Oracle自定义的外连接 5 SQL99标准的交叉连接 6 SQL99标准的自然连接 7 SQL99标准的内连接 8 子查询 9 union和intersect
  • 刷爆 LeetCode 双周赛 100,单方面宣布第一题最难

    上周末是 LeetCode 第 100 场双周赛 你参加了吗 这场周赛整体没有 Hard 题 但是也没有 Easy 题 第一题国服前百名里超过一半人 wa 很少见 小彭的技术交流群 02 群来了 公众号回复 加群 加入我们 周赛概览 259