首页 > 其他分享 >【每日一题】LeetCode 2398.预算内的最多机器人数目(滑动窗口、数组、二分查找、前缀和、堆(优先队列))

【每日一题】LeetCode 2398.预算内的最多机器人数目(滑动窗口、数组、二分查找、前缀和、堆(优先队列))

时间:2024-09-13 18:55:18浏览次数:3  
标签:right 窗口 chargeTimes int sum 机器人 预算内 LeetCode 2398

【每日一题】LeetCode 2398.预算内的最多机器人数目(滑动窗口、数组、二分查找、前缀和、堆(优先队列))

题目描述

给定两个整数数组 chargeTimesrunningCosts,分别代表 n 个机器人的充电时间和运行成本。再给定一个整数 budget,表示预算。我们需要计算在不超过预算的情况下,最多可以连续运行多少个机器人。

运行 k 个机器人的总开销计算公式为:max(chargeTimes) + k * sum(runningCosts),其中 max(chargeTimes) 是这 k 个机器人中最大充电时间,sum(runningCosts) 是这 k 个机器人的运行时间之和。

思路分析

这个问题可以通过滑动窗口的方法来解决。我们的目标是在不超过预算的情况下,找到能够连续运行的最多机器人数量。

  1. 初始化两个指针 leftright 分别指向数组的起始位置,以及一些必要的变量,如 maxRobots 用于存储最大机器人数量,maxChargeTimes 用于存储窗口内的最大充电时间,sum 用于存储窗口内运行成本的总和。

  2. 使用 right 指针扩展窗口,将 right 指针所指的机器人加入到当前窗口中,更新 maxChargeTimessum

  3. 检查当前窗口的总开销是否超过预算。如果超过预算,通过移动 left 指针缩小窗口,直到窗口内的总开销不超过预算。

  4. 在每一步中,更新 maxRobots,记录下当前窗口内能够运行的最大机器人数量。

  5. 重复步骤 2-4,直到 right 指针遍历完整个数组。

  6. 返回 maxRobots 作为最终结果。

输入示例

  • 示例 1:

    • 输入:chargeTimes = [3,6,1,3,4], runningCosts = [2,1,3,4,5], budget = 25
    • 输出:3
    • 解释:可以在 budget 以内运行所有单个机器人或者连续运行 2 个机器人。选择前 3 个机器人,可以得到答案最大值 3 。总开销是 max(3,6,1) + 3 * sum(2,1,3) = 6 + 3 * 6 = 24 ,小于 25 。可以看出无法在 budget 以内连续运行超过 3 个机器人,所以我们返回 3 。
  • 示例 2:

    • 输入:chargeTimes = [11,12,19], runningCosts = [10,8,7], budget = 19
    • 输出:0
    • 解释:即使运行任何一个单个机器人,还是会超出 budget,所以我们返回 0 。

代码实现

class Solution {
    public int maximumRobots(int[] chargeTimes, int[] runningCosts, long budget) {
        int n = chargeTimes.length;
        int maxRobots = 0; // 最多可以连续运行的机器人数目
        int maxChargeTimes = 0; // 当前窗口内的最大充电时间
        long sum = 0; // 当前窗口内的运行成本总和
        int left = 0; // 滑动窗口的左边界
        int right = 0; // 滑动窗口的右边界

        while (right < n) {
            // 扩展窗口,将当前机器人加入窗口
            maxChargeTimes = Math.max(maxChargeTimes, chargeTimes[right]);
            sum += runningCosts[right];

            // 当前窗口的总开销 = 最大充电时间 + 当前窗口内机器人数量 * 运行成本总和
            while (maxChargeTimes + (right - left + 1) * sum > budget) {
                // 如果当前窗口的总开销超过预算,缩小窗口
                sum -= runningCosts[left];
                left++;

                // 重新计算窗口内的最大充电时间
                maxChargeTimes = 0;
                for (int i = left; i <= right; i++) {
                    maxChargeTimes = Math.max(maxChargeTimes, chargeTimes[i]);
                }
            }
            // 更新最大机器人数量
            maxRobots = Math.max(maxRobots, right - left + 1);
            right++;
        }

        return maxRobots;
    }
}

请添加图片描述

优化思路分析

使用双端队列(Deque)来维护一个单调递减的充电时间序列。这种方法可以更高效地处理窗口的扩展和收缩,从而减少不必要的计算。

  1. 维护单调队列:使用一个双端队列 q 来维护当前窗口内机器人的索引,这个队列按照机器人的充电时间从大到小的顺序排列。这样可以快速找到当前窗口内最大充电时间的机器人。

  2. 扩展窗口:遍历每个机器人,将其索引加入到队列中。如果队列尾部的机器人充电时间小于当前机器人的充电时间,则将其移除,因为当前机器人的充电时间更大,可以替换掉队列尾部的机器人。

  3. 计算运行成本总和:随着窗口的扩展,累加当前窗口内所有机器人的运行成本。

  4. 收缩窗口:如果当前窗口的总开销超过了预算,通过移除窗口左侧的机器人来缩小窗口,直到总开销不超过预算。同时,更新运行成本总和。

  5. 更新最大机器人数量:在每一步中,更新最大机器人数量 ans,记录下当前窗口内能够运行的最大机器人数量。

  6. 返回结果:遍历完成后,返回记录的最大机器人数量。

优化后的代码实现

import java.util.ArrayDeque;
import java.util.Deque;

class Solution {
    public int maximumRobots(int[] chargeTimes, int[] runningCosts, long budget) {
        int ans = 0; // 记录最大机器人数量
        int left = 0; // 滑动窗口的左边界
        long sum = 0; // 当前窗口内的运行成本总和
        Deque<Integer> q = new ArrayDeque<>(); // 用于维护单调递减的充电时间序列

        for (int right = 0; right < chargeTimes.length; right++) {
            // 1. 扩展窗口
            while (!q.isEmpty() && chargeTimes[right] >= chargeTimes[q.peekLast()]) {
                q.pollLast(); // 移除队列尾部充电时间较小的机器人
            }
            q.addLast(right); // 将当前机器人加入队列
            sum += runningCosts[right]; // 更新运行成本总和

            // 2. 收缩窗口
            while (!q.isEmpty() && chargeTimes[q.peekFirst()] + (right - left + 1) * sum > budget) {
                if (q.peekFirst() == left) {
                    q.pollFirst(); // 移除队列头部的机器人
                }
                sum -= runningCosts[left++]; // 更新运行成本总和
            }

            // 3. 更新答案
            ans = Math.max(ans, right - left + 1); // 更新最大机器人数量
        }
        return ans; // 返回最终结果
    }
}

请添加图片描述

标签:right,窗口,chargeTimes,int,sum,机器人,预算内,LeetCode,2398
From: https://blog.csdn.net/Hanbuhuic/article/details/142217894

相关文章

  • leetcode刷题day17|二叉树Part05(654.最大二叉树、617.合并二叉树、700.二叉搜索树中的
    654.最大二叉树构造树一般采用的是前序遍历,因为先构造中间节点,然后递归构造左子树和右子树。递归三步曲:1、传入参数,整数数组,数组的开始和结束,返回树的中间节点。2、终止条件:开始大于等于结束,即数组为空。3、递归逻辑:找到最大的元素,记录元素其下标,构建中间节点;递归构造......
  • LeetCode算法—分治法
    思路:分治法的核心思想是“分而治之”,即将一个复杂的问题分成多个较小的子问题,分别求解这些子问题,然后将子问题的解合并,得到原问题的解。具体到求众数的问题上,分治法通过递归地将数组分成两部分,分别找出每一部分的众数,最后通过合并步骤来确定整个数组的众数。LeetCode169多数元素......
  • LeetCode 692.前K个高频单词
    LeetCode692.前K个高频单词C++思路......
  • 力扣刷题——2398. 预算内的最多机器人数目
    由题目中求“最多可以连续运行的机器人数目”可知,求的是数组子数组的长度,那么就可以直接使用滑动窗口求解。配合前缀和,可以快速的求得滑动窗口内的运行时间和。那么编写代码如下:intmaximumRobots(vector<int>&chargeTimes,vector<int>&runningCosts,longlongbudget){......
  • 【LeetCode Hot 100】1. 两数之和
    题目描述显然,最简单和直接的想法是使用暴力枚举:使用双重循环枚举符合条件的下标对并返回。这种方法的时间复杂度是平方级别\(O(N^2)\)。对于每个确定的数x,我们需要找到target-x对应的下标,暴力枚举方法使用的是直接遍历,这个操作的复杂度是线性的,而如果我们使用哈希表将元素及其......
  • 0910-0911 shell编程与基础算法(leetCode )
    栈的定义栈(Stack),也称为堆栈,它是一种特殊的线性表,只允许在表的一端进行插入和删除操作。允许在表操作的一端称为栈顶(Top),另一端称为栈底(Bottom)。栈顶是动态变化的,它由一个称为栈顶指针(top)的变量指示。当表中没有元素时,称为空栈。栈的插入操作称为入栈或进栈,删除操作称为出栈或......
  • LeetCode刷题
    2.11378.使用唯一标识码替换员工ID2.1.1说明Employees表:±--------------±--------+|ColumnName|Type|±--------------±--------+|id|int||name|varchar|±--------------±--------+在SQL中,id是这张表的主键。这张表的每一行分别代表了某......
  • leetcode刷题
    3.1力扣之1421-净现值查询3.1.1说明表:NPV±--------------±--------+|ColumnName|Type|±--------------±--------+|id|int||year|int||npv|int|±--------------±--------+(id,year)是该表主键(具有唯一值的列的组合).该表有每一笔存......
  • LeetCode: 1407. 排名靠前的旅行者
    排名靠前的旅行者原题表:Users+---------------+---------+|ColumnName|Type|+---------------+---------+|id|int||name|varchar|+---------------+---------+id是该表中具有唯一值的列。name是用户名字。表:Rides......
  • 题解 力扣 LeetCode 105 从前序与中序遍历序列构造二叉树 C/C++
    题目传送门:105.从前序与中序遍历序列构造二叉树-力扣(LeetCode)https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/description/每次在中序遍历序列片段中找到前序遍历序列队首,这是该层的根节点该位置左侧是左子树,右侧是右子树再......