LC_单调队列、动态凸包

题目
有n(1e5)个车子{pos, speed},pos表示距离x=0点的距离,speed为速度
这n个车子的pos 严格单调递增

比如 a{1, 5} b{7, 1}, 在1.5s秒后 这两个车子都到达了pos=8.5位置上
此时,就发生了“相撞“,导致: a车和b车 形成一个“车队”,车队速度为b车速度!
问所有车子,第一次形成车队的时间 (否则输出-1)

将问题抽象的能力,非常重要!!!
'其实根本没必要去”形成车队“'
a车发生追尾 和b车形成了车队,有2个信息:
1, a车的speed 一定> b车的speed
	只要存在:speed[ >i ] < speed[ i ]
	那么: i号车,一定会发生追尾
2: ab车形成了车队,一定有speed[a] > speed[b],且车队的speed = speed[b]	
	说明: 其实你的“追尾”,是产生不了任何效益的!!! b车还是正常的行驶
	你可以完全的看成: '从此,a车消失了,b车单独正常行驶'

'问题转换: '
对于cur号车: pos[cur] + (speed[cur] * time) == pos[nex] + (speed[nex]*time)
	->   time = (pos[nex] - pos[cur]) / (speed[cur] - speed[nex])
	->   即在所有的nex(1,nex > cur     2,speed[nex] < speed[cur])中找
	->   求最小的time,  即为ans[cur]的结果


' 问题转换: '
因为pos是'严格'递增的,所以 我们把pos当成x轴,speed当成y轴
那么,每一个小车 就对应这个坐标系上的很多点
	/ time = (x[?] - x[i]) / (y[i] - y[?])
	/  满足: (? > i) && (y[?] < y[i])的情况下, 找最小的time == ans[i]
这个式子,现在和'斜率式子 (y - y) / (x - x)'非常的相像,继续转换

	/ time1 = time的倒数 = (y[i] - y[?]) / (x[?] - x[i])
	/ time2 = time的倒数的负数 = (y[?] - y[i]) / (x[?] - x[i])
'此时这个time2,就是<标准的 斜率式子>; 并且time2是和time,成正比的!!!'
因为, 找最小 -> 倒数 -> 找最大 -> 负数 -> 还是找最小
我们找最小的time2, 那么ans[i] = time = time2的倒数的负数

那么此时'最小的time2'在这个几何坐标系,意味着什么呢?
最小的time2,即最小的斜率

我们要回顾 '斜率问题'
所有的直线 分为这4种类型!!!
所有的直线,
他与'x轴正方向'的夹角 一定是在[0, 180]内
对于第3类直线,他从(0 -> 90)度的过程 其tan值是从[0 -> +INF]的过程
   不要混淆成 tan是从[0 -> pi/2]的过程!!!  
   pi/2'弧度制',他和'角度'是一个东西!! 而tan值 和 弧度制没有关系!!
   弧度制/角度,只是tan的x轴,我们看的是y值
对于第4类直线,他是从(90 -> 180)度的过程 其tan值是从[-INF -> 0]的过程

在这里插入图片描述
在这里插入图片描述

我们此时要找的直线,是第4类直线!! 因为当前车速度必须要大于追尾车的速度
'即以当前车的垂直线,逆时针的旋转,找右侧第一个触碰的点'

动态维护凸包:  我们从右侧(FORR(i, n, 1, 1))开始,倒序的遍历
	1, 维护右侧的所有点的 '凸包',可以看出 当前点要找的点 一定是在这个凸包上
	2,朴素的凸包(一个封闭的图形)
我们没必要去维护整个凸包,因为可以看到 对于4,5,6,7这些线段 肯定不会是答案
即,只维护1,2,3这些线段即可。  
这里所谓的'维护线段',其实并不是维护的线段,而是维护'构成这些线段的点们'
比如,要维护123这些线段,只要依次的保存这4个点即可
 对于ABCD这个凸包,其实B点 一定不会是某个[A左侧点]的匹配点
 所以,只需维护ACD这3个点即可 (AB*AC的叉积<0,说明B是可以删除的)
我们要维护的凸包中,一定没有'斜率>0'的直线!!!
这种情况,如果有答案 则一定是A 而不会是B!!!
我们所维护的凸包,一定是形如这个样子!!
1, 所有的线段的斜率,都是负值!!!
2, 即从右往左,其斜率一定是减小的
像图中的ABCDEF,就是我们要维护的点。 他们 都是可能作为答案的!!!

比如图中的“find C”这个点, 意味着 他的答案 是C这个点
:此时,需要把AB都删除掉
1,删除A:  很简单,只要y值>=当前点的,说明是'斜率>=0的'直线,需要删掉
2,删除B:  通过叉积<0判断
最终形成的新凸包是: [find C] [C] [D] [E] [F] 
可以发现,他们仍然是符合我们的标准的
    vector<double> getCollisionTimes(vector<vector<int>>& A) {
int n = A.size();	// 1e5 
pair<int,int>* stk = new pair<int,int>[n + 5];
int aa = 0;
VE<double> ans(n);

int cx, cy;
FORR(i, n-1, 0, 1){
	cx = A[i][0], cy = A[i][1];
	while( (aa > 0) && (stk[aa - 1].SEC >= cy) ){ 
		-- aa;  '斜率是>=0的直线'
	}
	
	if(aa == 0){ ans[i] = -1; }
	else{
		while(aa >= 2){
			if( cross(stk[aa - 1].FIR - cx, stk[aa - 1].SEC - cy,
				stk[aa-2].FIR-stk[aa-1].FIR, stk[aa-2].SEC-stk[aa-1].SEC) < 0 ){
				-- aa; '凹包'
			}else break;
		}
		ans[i] = (double)(stk[aa - 1].FIR - cx) / (double)(cy - stk[aa-1].SEC);
	}
          stk[aa ++] = MPR(cx, cy);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值