LeetCode 2594. Minimum Time to Repair Cars【数组,二分】1915

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库:https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

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

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

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

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

示例 1:

js 复制代码
输入:ranks = [4,2,3,1], cars = 10
输出:16
解释:
- 第一位机械工修 2 辆车,需要 4 * 2 * 2 = 16 分钟。
- 第二位机械工修 2 辆车,需要 2 * 2 * 2 = 8 分钟。
- 第三位机械工修 2 辆车,需要 3 * 2 * 2 = 12 分钟。
- 第四位机械工修 4 辆车,需要 1 * 4 * 4 = 16 分钟。
16 分钟是修理完所有车需要的最少时间。

示例 2:

js 复制代码
输入:ranks = [5,1,8], cars = 6
输出:16
解释:
- 第一位机械工修 1 辆车,需要 5 * 1 * 1 = 5 分钟。
- 第二位机械工修 4 辆车,需要 1 * 4 * 4 = 16 分钟。
- 第三位机械工修 1 辆车,需要 8 * 1 * 1 = 8 分钟。
16 分钟时修理完所有车需要的最少时间。

提示:

  • 1 <= ranks.length <= 10^5
  • 1 <= ranks[i] <= 100
  • 1 <= cars <= 10^6

解法 二分+优化

注意,能力值越低,修车越快。(按照英文描述, r a n k s i ranks_i ranksi 应该翻译成「排名」,排名越靠前,修车越快)。

如果已知修车的时间 t t t,我们可以算出每个人在 t t t 分钟内能修好多少辆车。例如一个能力值 r = 3 r=3 r=3 的人可以在 t = 16 t=16 t=16 分钟内修好 2 2 2 辆车,但无法修好 3 3 3 辆车。

根据题意,需要满足
r n 2 ≤ t rn^2 \le t rn2≤t解得
n ≤ t r n\le \sqrt {\dfrac{t}{r}} n≤rt

所以能力值为 r r r 的机械工最多可以修
⌊ t r ⌋ \left\lfloor\sqrt {\dfrac{t}{r}}\right\rfloor ⌊rt ⌋

​辆车。

累加每个人在 t t t 分钟内能修的车的个数,如果
∑ i = 0 i = n − 1 ⌊ t ranks [ i ] ⌋ ≥ cars \sum_{i=0}^{i=n-1} \left\lfloor\sqrt {\dfrac{t} {\textit{ranks}[i]} }\right\rfloor \ge \textit{cars} i=0∑i=n−1⌊ranks[i]t ⌋≥cars

则说明可以在 t t t 分钟修完所有车。

上式表明,t t t 越大,能修的车越多,越能满足要求。有了这样的单调性,我们就可以二分答案了。

二分上界为 min ⁡ ( ranks ) ⋅ cars 2 \min(\textit{ranks}) \cdot \textit{cars}^2 min(ranks)⋅cars2 ,即让能力值最低(修车最快)的人修好所有车所需要的时间

问:开方直接取整的做法是否会有精度误差?

答:代码中对整数开方,只要整数转浮点没有丢失精度(在 2 53 − 1 2^{53}-1 253−1 内),开方出来的整数部分就是正确的。具体可以参考 IEEE 754。

cpp 复制代码
class Solution {
public:
    long long repairCars(vector<int> &ranks, int cars) {
        int min_r = *min_element(ranks.begin(), ranks.end());
        long long left = 0, right = 1LL * min_r * cars * cars;
        while (left + 1 < right) { // 开区间
            long long mid = (left + right) / 2, s = 0;
            for (int r : ranks)
                s += sqrt(mid / r);
            (s >= cars ? right : left) = mid;
        }
        return right;
    }
};

复杂度分析:

  • 时间复杂度: O ( n log ⁡ ( m c 2 ) ) \mathcal{O}(n\log(mc^2)) O(nlog(mc2)) ,其中 n n n 为 ranks \textit{ranks} ranks 的长度, m = min ⁡ ( ranks ) m=\min(\textit{ranks}) m=min(ranks) , c = cars c=\textit{cars} c=cars 。
  • 空间复杂度: O ( 1 ) \mathcal{O}(1) O(1) 。仅用到若干额外变量。

解法2 优化

能力值相同的人,在 t t t 分钟内修好的车的个数是一样的。

根据数据范围, ranks \textit{ranks} ranks 中至多有 100100100 个不同的数字,我们可以统计 r a n k s ranks ranks 中每个数字的出现次数,这样每次二分中至多循环 100 100 100 次。

此外,如果循环中发现 s ≥ cars s\ge \textit{cars} s≥cars ,可以提前退出循环

cpp 复制代码
class Solution {
public:
    long long repairCars(vector<int> &ranks, int cars) {
        int min_r = ranks[0], cnt[101]{}; // 数组比哈希表更快
        for (int r : ranks) {
            min_r = min(min_r, r);
            cnt[r]++;
        }
        long long left = 0, right = 1LL * min_r * cars * cars;
        while (left + 1 < right) {
            long long mid = (left + right) / 2, s = 0;
            for (int r = min_r; r <= 100 && s < cars; r++) // 至多循环 100 次
                s += (long long) sqrt(mid / r) * cnt[r];
            (s >= cars ? right : left) = mid;
        }
        return right;
    }
};

复杂度分析:

  • 时间复杂度: O ( n + M log ⁡ ( m c 2 ) ) \mathcal{O}(n + M\log(mc^2)) O(n+Mlog(mc2)) ,其中 n n n 为 ranks \textit{ranks} ranks 的长度 M = max ⁡ ( ranks ) M=\max(\textit{ranks}) M=max(ranks) , m = min ⁡ ( ranks ) m=\min(\textit{ranks}) m=min(ranks) , c = c a r s c=cars c=cars 。
  • 空间复杂度: O ( M ) \mathcal{O}(M) O(M) 。
相关推荐
王老师青少年编程3 小时前
gesp(C++五级)(14)洛谷:B4071:[GESP202412 五级] 武器强化
开发语言·c++·算法·gesp·csp·信奥赛
DogDaoDao3 小时前
leetcode 面试经典 150 题:有效的括号
c++·算法·leetcode·面试··stack·有效的括号
Coovally AI模型快速验证4 小时前
MMYOLO:打破单一模式限制,多模态目标检测的革命性突破!
人工智能·算法·yolo·目标检测·机器学习·计算机视觉·目标跟踪
可为测控4 小时前
图像处理基础(4):高斯滤波器详解
人工智能·算法·计算机视觉
Milk夜雨5 小时前
头歌实训作业 算法设计与分析-贪心算法(第3关:活动安排问题)
算法·贪心算法
BoBoo文睡不醒5 小时前
动态规划(DP)(细致讲解+例题分析)
算法·动态规划
apz_end6 小时前
埃氏算法C++实现: 快速输出质数( 素数 )
开发语言·c++·算法·埃氏算法
仟濹6 小时前
【贪心算法】洛谷P1106 - 删数问题
c语言·c++·算法·贪心算法
银河梦想家7 小时前
【Day23 LeetCode】贪心算法题
leetcode·贪心算法
CM莫问7 小时前
python实战(十五)——中文手写体数字图像CNN分类
人工智能·python·深度学习·算法·cnn·图像分类·手写体识别