LeetCode 2532 过桥的时间

文章描述了一个涉及员工搬箱子过桥的问题,员工之间的操作互不影响。通过模拟桥的空闲时间和员工的动作,使用优先队列来管理等待过桥的员工和事件,计算最小的过桥时间。算法考虑了员工从左往右和从右往左过桥的情况,以及搬运新旧箱子所需的时间,并用最小堆优化处理这些事件。
摘要由CSDN通过智能技术生成

题目链接
在这里插入图片描述
在这里插入图片描述


模拟: 因为各员工搬箱子这件事相互之间没有影响, 即一个员工 i i i开始从左往右过桥时, 可以产生两个事件:

  • l e f t T o R i g h t i leftToRight_i leftToRighti 分钟后桥空闲(若两岸有再等待过桥的人, 应该按规则过桥)
  • l e f t T o R i g h t i + p i c k O l d i leftToRight_i+pickOld_i leftToRighti+pickOldi 分钟后工人 i i i开始等待从右往左地过桥

类似的, 各员工放箱子这件事相互之间没有影响, 即一个员工 i i i开始从右往左过桥时, 可以产生两个事件:

  • r i g h t T o L e f t i rightToLeft_i rightToLefti 分钟后桥空闲(若两岸有再等待过桥的人, 应该按规则过桥)
  • r i g h t T o L e f t i + p i c k N e w i rightToLeft_i+pickNew_i rightToLefti+pickNewi 分钟后工人 i i i开始等待从左往右地过桥

所以可以枚举桥空闲的时间, 同时用两个堆维护当前时间两岸等待过桥的工人, 按规则选择过桥的人, 用两个堆来维护这个人"过桥时间+搬/放 箱子时间"之后等待再次过桥这个事件, 具体的一些细节可以看代码.


struct S {//工人类
    S(int lr_ = 0, int po_ = 0, int rl_ = 0, int pn_ = 0, int ind_ = 0) : lr(lr_), po(po_), rl(rl_), pn(pn_), ind(ind_) {}

    int lr;//leftToRight[i]
    int po;//pickOldi[i]
    int rl;//rightToLeft[i]
    int pn;//putNew[i]
    int ind;//i

    friend bool operator<(const S &a, const S &b) {//a的优先级低于b则返回true否则返回false
        if (a.lr + a.rl != b.lr + b.rl)
            return a.lr + a.rl > b.lr + b.rl;
        return a.ind > b.ind;
    }

    friend bool operator>(const S &a, const S &b) {
        return !(a < b);
    }
};

class Solution {
public:
    int findCrossingTime(int n, int k, vector<vector<int>> &time) {
        queue<int> bridge;//维护桥的空闲时间
        priority_queue<S, vector<S>, greater<>> left, right;//最小堆维护两岸当前空闲的工人, 效率最低的在堆顶
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> leftwork, rightwork;//最小堆维护各工人"过桥时间+搬/放 箱子时间"之后等待再次过桥这个事件. 例: pair(time, index)表示若当前时间不小time则下标为index的工人应该加入等待过桥的堆中

        auto heap_insert = [&](priority_queue<S, vector<S>, greater<>> &heap, int i) {//将下标为i的工人加入堆heap中
            heap.emplace(time[i][0], time[i][1], time[i][2], time[i][3], i);
        };
        for (int i = 0; i < k; i++)//初始情况所有人都在左岸
            heap_insert(left, i);
        bridge.push(0);//初始情况桥空闲时间为0
        int cnt_new = 0;//到达左岸的旧箱子数
        int cnt_old = n;//尚未确定有人搬的旧箱子数
        while (1) {
            int cur;//当前时间
            if (!bridge.empty()) {//桥一空闲立即判断两岸是否有在等待的人
                cur = bridge.front();
                bridge.pop();
            } else {//特殊情况: 桥上一次刚空闲时两岸都没有等待过桥的人, 则当前时间增加到两岸搬放最早结束的时间
                if (!leftwork.empty() && !rightwork.empty())
                    cur = min(leftwork.top().first, rightwork.top().first);
                else if (!leftwork.empty())
                    cur = leftwork.top().first;
                else
                    cur = rightwork.top().first;
            }
            while (!leftwork.empty() && leftwork.top().first <= cur) {//处理当前时间左岸放箱子结束需要加入等待过桥的工人
                heap_insert(left, leftwork.top().second);
                leftwork.pop();
            }
            while (!rightwork.empty() && rightwork.top().first <= cur) {//处理当前时间右岸搬箱子结束需要加入等待过桥的工人
                heap_insert(right, rightwork.top().second);
                rightwork.pop();
            }
            if (!right.empty()) {//右岸有人等待则右岸优先
                auto t = right.top();
                right.pop();
                if (++cnt_new == n)
                    return cur + t.rl;
                bridge.push(cur + t.rl);//cur+t.rl后桥空闲
                leftwork.emplace(cur + t.rl + t.pn, t.ind);//cur+t.rl+t.pn后t加入等待过桥的堆
            } else if (!left.empty() && cnt_old) {//右岸无人等待, 左岸有人且还有尚未确定谁搬的旧箱子
                auto t = left.top();
                left.pop();
                bridge.push(cur + t.lr);//cur+t.lr后桥空闲
                cnt_old--;
                rightwork.emplace(cur + t.lr + t.po, t.ind);//cur+t.lr+t.po后t加入等待过桥的堆
            }
        }
    }
};

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值