题目大意:
一维平面上给你 n n n个汽车在位置 p i p_i pi,以速度 v i v_i vi往右行驶。当两车相遇的时候,速度变为两车最慢速度。现在问你每个车它与下一辆车(车队)相遇的时间。
n ≤ 1 e 5 n \leq 1e5 n≤1e5
题目思路:
显然我们有一个暴力的 n 2 n^2 n2,对每个车,求出它与后面那些车相遇的最短时间.
画在 s − v s-v s−v二维坐标系中,我们就是要求 t = s j − s i v i − v j t=\frac{s_j-s_i}{v_i-v_j} t=vi−vjsj−si的最小值.那么就是求点对之间的斜率最大值.
分析过程:
这个我们可以利用单调栈来实现:令入栈顺序为: C n , C n − 1 , . . . , C 1 C_n,C_{n-1},...,C_1 Cn,Cn−1,...,C1
假设当前点为 C i C_i Ci,栈顶元素 C x C_x Cx,
0.若栈为空,则它不会与任何车相遇。
1.若其速度大于等于 C i C_i Ci,则它俩永远不会相遇,则它对 C i C_i Ci的答案的更新没有作用,也对之后的点没有作用。(因为 C i C_i Ci是 C x , x ∈ [ 1 , i ] C_x,x\in[1,i] Cx,x∈[1,i]的速度瓶颈,后面的车速度无法超过 C i C_i Ci).所以将其弹出栈顶.
2.1否则,假设栈顶元素和它的最大斜率配对为: ( C x , C y ) , x < y (C_x,C_y),x<y (Cx,Cy),x<y.那么如果 k i , x < k x , y k_{i,x}<k_{x,y} ki,x<kx,y.则 k i , y > k i , x k_{i,y}>k_{i,x} ki,y>ki,x( k k k为点对斜率).这种情况用白话讲就是: C i C_i Ci会在 C x C_x Cx和 C y C_y Cy相遇之后再汇合。所以对于 C i C_i Ci来讲,瓶颈就是 C y C_y Cy.所以这个时候我们就需要将 C x C_x Cx弹出栈,继续这个过程,如下图所示:
2.2 反之的话, k i , y < k i , x k_{i,y} < k_{i,x} ki,y<ki,x.这种情况用白话讲就是: C i C_i Ci会在 C x C_x Cx和 C y C_y Cy相遇之前与 C x C_x Cx汇合。那么答案就是 ( C i , C x ) (C_i,C_x) (Ci,Cx).这个时候停止这个过程即可.如下图所示:
2.3 结束这个过程后将 i i i入栈.
从上面两个过程可以看出,我们的单调栈就会维护一个下凸包.因为这样是最优的。
class Solution {
public:
stack<int> s;
double calc(int i , int j , vector<vector<int>>& a){
return 1.0 * (a[j][0] - a[i][0]) / (a[i][1] - a[j][1]);
}
vector<double> getCollisionTimes(vector<vector<int>>& a) {
vector<double> ans;
int n = a.size();
ans.resize(n);
ans[n - 1] = -1;
for (int i = 0 ; i < n ; i++){
double res = 1e9;
for (int j = i + 1 ; j < n ; j++){
if (a[i][1] < a[j][1]) continue;
res = min (res , calc(i , j , a));
}
if (res == 1e9) ans[i] = -1;
else ans[i] = res;
}
return ans;
}
};