题目链接
模拟: 因为各员工搬箱子这件事相互之间没有影响, 即一个员工 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加入等待过桥的堆
}
}
}
};