【2532. 过桥的时间】

文章描述了一个涉及k个工人搬运n个箱子从旧仓库到新仓库的问题,通过分析工人过桥和搬运箱子的时间,利用优先队列策略优化处理工人状态,以求得最后一个工人返回左岸的时间。算法主要关注工人的效率和过桥规则,时间复杂度为O(nlogk),空间复杂度为O(k)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

来源:力扣(LeetCode)

描述:

共有 k 位工人计划将 n 个箱子从旧仓库移动到新仓库。给你两个整数 nk,以及一个二维整数数组 time ,数组的大小为 k x 4 ,其中 time[i] = [leftToRighti, pickOldi, rightToLefti, putNewi]

一条河将两座仓库分隔,只能通过一座桥通行。旧仓库位于河的右岸,新仓库在河的左岸。开始时,所有 k 位工人都在桥的左侧等待。为了移动这些箱子,第 i 位工人(下标从 0 开始)可以:

  • 从左岸(新仓库)跨过桥到右岸(旧仓库),用时 leftToRighti 分钟。
  • 从旧仓库选择一个箱子,并返回到桥边,用时 pickOldi 分钟。不同工人可以同时搬起所选的箱子。
  • 从右岸(旧仓库)跨过桥到左岸(新仓库),用时 rightToLefti 分钟。
  • 将箱子放入新仓库,并返回到桥边,用时 putNewi 分钟。不同工人可以同时放下所选的箱子。

如果满足下面任一条件,则认为工人 i 的 效率低于 工人 j :

  • leftToRighti + rightToLefti > leftToRightj + rightToLeftj
  • leftToRighti + rightToLefti == leftToRightj + rightToLeftji > j

工人通过桥时需要遵循以下规则:

  • 如果工人 x 到达桥边时,工人 y 正在过桥,那么工人 x 需要在桥边等待。
  • 如果没有正在过桥的工人,那么在桥右边等待的工人可以先过桥。如果同时有多个工人在右边等待,那么 效率最低 的工人会先过桥。
  • 如果没有正在过桥的工人,且桥右边也没有在等待的工人,同时旧仓库还剩下至少一个箱子需要搬运,此时在桥左边的工人可以过桥。如果同时有多个工人在左边等待,那么 效率最低 的工人会先过桥。

所有 n 个盒子都需要放入新仓库,请你返回最后一个搬运箱子的工人 到达河左岸 的时间。

示例 1:

输入:n = 1, k = 3, time = [[1,1,2,1],[1,1,3,1],[1,1,4,1]]
输出:6
解释:
从 01 :工人 2 从左岸过桥到达右岸。
从 12 :工人 2 从旧仓库搬起一个箱子。
从 26 :工人 2 从右岸过桥到达左岸。
从 67 :工人 2 将箱子放入新仓库。
整个过程在 7 分钟后结束。因为问题关注的是最后一个工人到达左岸的时间,所以返回 6

示例 2:

输入:n = 3, k = 2, time = [[1,9,1,8],[10,10,10,10]]
输出:50
解释:
从 010 :工人 1 从左岸过桥到达右岸。
从 1020 :工人 1 从旧仓库搬起一个箱子。
从 1011 :工人 0 从左岸过桥到达右岸。
从 1120 :工人 0 从旧仓库搬起一个箱子。
从 2030 :工人 1 从右岸过桥到达左岸。
从 3040 :工人 1 将箱子放入新仓库。
从 3031 :工人 0 从右岸过桥到达左岸。
从 3139 :工人 0 将箱子放入新仓库。
从 3940 :工人 0 从左岸过桥到达右岸。
从 4049 :工人 0 从旧仓库搬起一个箱子。
从 4950 :工人 0 从右岸过桥到达左岸。
从 5058 :工人 0 将箱子放入新仓库。
整个过程在 58 分钟后结束。因为问题关注的是最后一个工人到达左岸的时间,所以返回 50

提示:

  • 1 <= n, k <= 104
  • time.length == k
  • time[i].length == 4
  • 1 <= leftToRighti, pickOldi, rightToLefti, putNewi <= 1000

方法:优先队列

思路与算法

在本题中,工人共有 4 种状态:

  1. 在左侧等待
  2. 在右侧等待
  3. 在左侧工作(放下所选箱子)
  4. 在右侧工作(搬起所选箱子)

每一种工作状态都有相应的优先级计算方法,因此我们用 4 个优先队列来存放处于每种状态下的工人集合:

  1. 在左侧等待的工人:wait_left,题目中定义的效率越高,优先级越高。
  2. 在右侧等待的工人:wait_right,题目中定义的效率越高,优先级越高。
  3. 在左侧工作的工人:work_left,完成时间越早,优先级越高。
  4. 在右侧工作的工人:work_right,完成时间越早,优先级越高。

我们令 remain 表示右侧还有多少个箱子需要搬运,当 remain > 0 时,搬运工作需要继续。除此之外,题目求解的是最后一个回到左边的工人的到达时间,因此当右侧还有工人在等待或在工作时(即 work_right 或 wait_right 不为空),搬运工作就需要继续:

  1. 若 work_left 或 work_right 中的工人在此刻已经完成工作,则需要将它们取出并分别加入到 wait_left 和 wait_right 中。
  2. 若 wait_right 不为空,则取其中优先级最低的工人过桥,将其加入到 work_left 队列中,并将时间更改为过桥后的时间。继续下一轮循环。
  3. 若 remain > 0,并且 wait_left 不为空,则需要取优先级最低的工人过桥去取箱子,将其加入到 work_right 队列中,令 remain 减 1,并将时间更改为过桥后的时间。继续下一轮循环。
  4. 若 2 和 3 都不满足,表示当前没有人需要过桥,当前时间应该过渡到 work_left 和 work_right 中的最早完成时间。然后继续下一轮循环。

按照上述过程模拟,直到循环条件不再满足。

代码:

class Solution {
public:
    using PII = pair<int, int>;
    int findCrossingTime(int n, int k, vector<vector<int>>& time) {
        // 定义等待中的工人优先级比较规则,时间总和越高,效率越低,优先级越低,越优先被取出
        auto wait_priority_cmp = [&](int x, int y) {
            int time_x = time[x][0] + time[x][2];
            int time_y = time[y][0] + time[y][2];
            return time_x != time_y ? time_x < time_y : x < y;
        };

        priority_queue<int, vector<int>, decltype(wait_priority_cmp)> wait_left(wait_priority_cmp), wait_right(wait_priority_cmp);

        priority_queue<PII, vector<PII>, greater<PII>> work_left, work_right;

        int remain = n, cur_time = 0;
        for (int i = 0; i < k; i++) {
            wait_left.push(i);
        }
        while (remain > 0 || !work_right.empty() || !wait_right.empty()) {
            // 1. 若 work_left 或 work_right 中的工人完成工作,则将他们取出,并分别放置到 wait_left 和 wait_right 中。
            while (!work_left.empty() && work_left.top().first <= cur_time) {
                wait_left.push(work_left.top().second);
                work_left.pop();
            }
            while (!work_right.empty() && work_right.top().first <= cur_time) {
                wait_right.push(work_right.top().second);
                work_right.pop();
            }

            if (!wait_right.empty()) {
                // 2. 若右侧有工人在等待,则取出优先级最低的工人并过桥
                int id = wait_right.top();
                wait_right.pop();
                cur_time += time[id][2];
                work_left.push({cur_time + time[id][3], id});
            } else if (remain > 0 && !wait_left.empty()) {
                // 3. 若右侧还有箱子,并且左侧有工人在等待,则取出优先级最低的工人并过桥
                int id = wait_left.top();
                wait_left.pop();
                cur_time += time[id][0];
                work_right.push({cur_time + time[id][1], id});
                remain--;
            } else {
                // 4. 否则,没有人需要过桥,时间过渡到 work_left 和 work_right 中的最早完成时间
                int next_time = INT_MAX;
                if (!work_left.empty()) {
                    next_time = min(next_time, work_left.top().first);
                }
                if (!work_right.empty()) {
                    next_time = min(next_time, work_right.top().first);
                }
                if (next_time != INT_MAX) {
                    cur_time = max(next_time, cur_time);
                }
            }
        }
        return cur_time;
    }
};

执行用时:148 ms, 在所有 C++ 提交中击败了60.27%的用户
内存消耗:20.5 MB, 在所有 C++ 提交中击败了94.52%的用户
复杂度分析
时间复杂度:O(nlogk),其中 n 为箱子的个数,k 为工人的个数。过程中每个队列最多会进出元素 n 次,每次进出的时间复杂度为 O(logk),因此总的时间复杂度为 O(nlogk)。
空间复杂度:O(k)。过程中每个优先队列最多会包含 k 个元素,因此总的空间复杂度为 O(k)。
author:LeetCode-Solution

回答: 过桥问题是一个经典的计算机科学问题,可以使用Python来解决。 首先,我们需要先了解问题的背景和规则: 假设在一个夜晚,4个人需要过一座桥,过桥的时候需要一盏灯照明,每个人的速度不同,第一个人需要1分钟过桥,第二个人需要2分钟,第三个人需要5分钟,第四个人需要10分钟。现在有一盏灯,每次只能由1人或2人带着过桥,两个人一起走时,速度和时间按照走得慢的那个人来计算。请问最快多少时间可以让他们全部过桥? 下面是一个使用Python解决过桥问题的具体实现: ```python import itertools def calculate_time(persons): # 计算一组人过桥时间 time = 0 for p in persons: time = max(time, p) return time def cross_bridge(persons): # 计算所有人过桥的最小时间 n = len(persons) min_time = float('inf') for i in range(1, n + 1): for group in itertools.combinations(persons, i): # 一人过桥 if len(group) == 1: time = calculate_time(group) # 两人过桥 else: t1 = max(group) t2 = min(group) time = t1 + t2 # 更新最小时间 if time < min_time: min_time = time return min_time persons = [1, 2, 5, 10] print(cross_bridge(persons)) # 输出最小时间 ``` 在这个代码中,我们使用了Python的itertools模块中的combinations函数来枚举所有人过桥的方案,然后计算每个方案的时间,最后返回所有方案中最小的时间。 注意,这个算法只适用于人数较少的情况,如果人数很多,那么计算量会非常大,这时需要使用更加高效的算法来解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千北@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值