2023.12.2
Problem: 1094. 拼车
思路
方法一:刚好最近在刷优先队列相关的题目,也做到挺多,拿到题目,观察到车沿一个方向前进,那么到每一个地方(from or to
)也就有确定的顺序,这个时候我们可以维护一个优先队列,当遇到最小的 from
就压入,此时弹出刚好小于等于 from
的to
。
方法二:AC后刷题解,官方并没给出上述解法,而是差分数组,这也是我第一次听到这个。换个说法,就是前缀和的逆,一般来说是 S[n-1] + A[n] = S[n]
,这里变成 A[n] = S[n] - S[n-1]
,那这样的话就很容易理解了,我们先用一个数组 diff[i]
记录第 i
个站的净上下车乘客量,再通过 diff[i]
计算 count[i]
,当 count[i] > capacity
时,则超载。
解题方法
方法一:优先队列
- 先对
from
进行升序排序,保证入队时是最小的from
先入; - 增加一个
cnt
记录当前的总乘客数; - 扫描
trips
,将当前from
处的vector
压入优先队列中,弹出当前的等于from
的to
,压入时(上车)增加乘客数cnt
,弹出时(下车)减少乘客数cnt
;
(此时的to
一定能保证最小,因为to
是从0~from
依次弹出的,并且车沿一个方向,也就有to > from
) - 若
cnt
大于capacity
(车容量),则超载,否则成功。
方法二:差分数组
- 先遍历
trips
计算出diff
,遍历过程中记录一个to
的最大值to_max
即最远的站,便于后续遍历diff
求count
; - 遍历
diff
求count
,当count[i] > capacity
时超载。
为了简化,我们只需记录count[i]
,因为count[i-1]
并不会用到,只有当前状态才会用到,因此只需要一个变量cnt
记录当前通过diff
得到的前缀和,得到立即判断即可。
复杂度
方法一: O ( n l o g n ) O(nlogn) O(nlogn),其中 n = l e n ( t r i p s ) n=len(trips) n=len(trips)
方法二: O ( n + l ) O(n+l) O(n+l),其中 n = l e n ( t r i p s ) l = m a x ( t r i p s . t o ) n=len(trips)\quad l=max(trips.to) n=len(trips)l=max(trips.to)
Code
// 优先队列
class Solution {
public:
bool static cmp1(const vector<int> &a,const vector<int> &b) {
return a[1] < b[1];
}
class cmp2 {
public:
bool operator()(const vector<int> &a,const vector<int> &b) {
return a[2] > b[2];
}
};
bool carPooling(vector<vector<int>>& trips, int capacity) {
sort(trips.begin(), trips.end(), cmp1);
priority_queue<vector<int>, vector<vector<int>>, cmp2> pq;
int cap = 0;
for (int i=0; i<trips.size();i++) {
while (!pq.empty() && pq.top()[2] <= trips[i][1]) {
cap -= pq.top()[0];
pq.pop();
}
cap += trips[i][0];
if (cap > capacity) {
return false;
}
pq.push(trips[i]);
}
return true;
}
};
// 差分数组
class Solution {
public:
bool carPooling(vector<vector<int>>& trips, int capacity) {
int diff[1001] = { 0 };
int to_max = 0, cnt = 0;
for (auto &t: trips) {
to_max = max(to_max, t[2]);
diff[t[1]] += t[0];
diff[t[2]] -= t[0];
}
for (int i=0; i<=to_max; i++) {
cnt += diff[i];
if (cnt > capacity) return false;
}
return true;
}
};