当前位置: 首页 > article >正文

【10.7】队列-解预算内的最多机器人数目

一、题目

        你有 n 个机器人,给你两个下标从 0 开始的整数数组 chargeTimes 和 runningCosts ,两者长度都为 n 。第 i 个机器人充电时间为 chargeTimes[i] 单位时间,花费 runningCosts[i] 单位时间运行。再给你一个整数 budget 。

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

        请你返回在 不超过 budget 的前提下,你 最多 可以 连续 运行的机器人数目为多少。

示例 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 。

提示:

  • chargeTimes.length == runningCosts.length == n
  • 1 <= n <= 5 * 10^4
  • 1 <= chargeTimes[i], runningCosts[i] <= 10^5
  • 1 <= budget <= 10^15

二、解题思路

        我们需要找到在不超过 budget 的前提下,可以连续运行的机器人的最大数量。总开销的计算公式为:

        其中,k是连续运行的机器人数目。

核心思路
  1. 滑动窗口

    • 使用双指针维护一个滑动窗口,表示当前连续运行的机器人区间。

    • 窗口的左边界为 left,右边界为 right

  2. 单调队列

    • 使用单调队列维护当前窗口中的最大值(chargeTimes 的最大值)。

    • 单调队列的性质是队首始终是当前窗口的最大值。

  3. 窗口扩展与收缩

    • 扩展窗口:右指针 right 向右移动,将新的机器人加入窗口。

    • 收缩窗口:如果当前窗口的总开销超过 budget,则左指针 left 向右移动,缩小窗口。

  4. 计算总开销

    • 使用前缀和数组快速计算 runningCosts 的区间和。

    • 使用单调队列快速获取当前窗口的 chargeTimes 最大值。

三、代码实现

#include <iostream>
#include <vector>
#include <deque>
#include <algorithm>

using namespace std;

int maximumRobots(vector<int>& chargeTimes, vector<int>& runningCosts, long long budget) {
    int n = chargeTimes.size();
    int left = 0; // 滑动窗口的左边界
    long long sumRunningCosts = 0; // 当前窗口的 runningCosts 之和
    int maxRobots = 0; // 记录最大连续运行的机器人数目

    // 单调队列,存储 chargeTimes 的下标,队首是当前窗口的最大值
    deque<int> dq;

    // 遍历右边界
    for (int right = 0; right < n; ++right) {
        // 更新 runningCosts 的和
        sumRunningCosts += runningCosts[right];

        // 维护单调队列,确保队首是当前窗口的最大值
        while (!dq.empty() && chargeTimes[dq.back()] <= chargeTimes[right]) {
            dq.pop_back();
        }
        dq.push_back(right);

        // 计算当前窗口的总开销
        long long currentCost = chargeTimes[dq.front()] + (right - left + 1) * sumRunningCosts;

        // 如果总开销超过 budget,则收缩窗口
        while (currentCost > budget && left <= right) {
            sumRunningCosts -= runningCosts[left];
            if (dq.front() == left) {
                dq.pop_front(); // 如果队首是左边界,则移除
            }
            left++;
            if (left <= right) {
                currentCost = chargeTimes[dq.front()] + (right - left + 1) * sumRunningCosts;
            }
        }

        // 更新最大连续运行的机器人数目
        if (currentCost <= budget) {
            maxRobots = max(maxRobots, right - left + 1);
        }
    }

    return maxRobots;
}

int main() {
    // 示例 1
    vector<int> chargeTimes1 = {3, 6, 1, 3, 4};
    vector<int> runningCosts1 = {2, 1, 3, 4, 5};
    long long budget1 = 25;
    cout << "结果: " << maximumRobots(chargeTimes1, runningCosts1, budget1) << endl;

    return 0;
}

        通过滑动窗口和单调队列的结合,我们可以高效地解决这个问题。滑动窗口用于维护连续运行的机器人区间,单调队列用于快速获取区间内的最大值,从而动态计算总开销并调整窗口大小。

        时间复杂度:O(n)O(n),每个元素最多被加入和弹出单调队列一次。
        空间复杂度:O(n)O(n),用于存储单调队列。


http://www.kler.cn/a/537573.html

相关文章:

  • Unity-Mirror网络框架-从入门到精通之Discovery示例
  • 【算法】动态规划专题⑦ —— 多重背包问题 + 二进制分解优化 python
  • Docker 和 Docker Compose
  • 甘肃省医保刷脸设备激活步骤
  • C32.【C++ Cont】静态实现双向链表及STL库的list
  • 流媒体技术原理
  • 一键操作,完美解决办公问题!
  • layui组件库的年份选择器怎么设置区间超过区间不可点击
  • 基于Docker搭建ES集群,并设置冷热数据节点
  • 【Flink实战】Flink -C实现类路径配置与实现UDF Jar
  • linux上scp能不能取代rsync
  • 学习笔记十九:K8S生成pod过程
  • 国自然面上项目|多模态MRI影像组学模型对乳腺癌新辅助治疗疗效的早期预判研究|基金申请·25-02-08
  • Windows逆向工程入门之汇编开发框架解析
  • Axios 拦截器实现的原理
  • C++ 23 的栈踪迹库(stacktrace)
  • 深度洞察与精确匹配:基于HAI部署DeepSeekR1的公考岗位推荐与智能分析
  • XY2-100的Verilog实现
  • 阿里云宝塔在线安装步骤
  • DeepSeek底层揭秘——记忆网络与持续学习机制
  • 用户位置与IP属地:二者之间的关联与差异
  • 日志2025.2.8
  • 深度剖析 Redisson 分布式锁:原理、实现与应用实践
  • k8s部署go-fastdfs
  • OSPF基础(3):区域划分
  • Java 的 CopyOnWriteArrayList 和 Collections.synchronizedList 有什么区别?分别有什么优缺点?