撞车
[题目链接](https://www.nowcoder.com/practice/d8f779b417094dc1a7864712bb9b4c63)
思路
一条单向单车道上有 辆车,第
辆车位于
,速度为
。求至少移除几辆车,才能使剩余车辆不发生碰撞。
碰撞条件分析
考虑两辆车 (位置
)和
(位置
),其中
。在时刻
它们的位置分别为
和
。碰撞发生当且仅当存在
使得:
$$
解得 。由于
,所以
当且仅当
,即后车速度大于前车速度。
转化为最长非递减子序列
因此,要使所有剩余车辆不发生碰撞,按位置排序后,速度必须非递减。这等价于:
- 将所有车按位置
从小到大排序;
- 在速度序列上求最长非递减子序列(LNDS)的长度
;
- 答案为
。
求 LNDS
最长非递减子序列可以用与 LIS 类似的 贪心 + 二分方法求解。维护一个数组
,其中
表示长度为
的非递减子序列的最小末尾值。对于每个速度
:
- 用
upper_bound找到中第一个严格大于
的位置;
- 若不存在,说明
可以接在最长子序列后面,将
追加到
;
- 否则,用
替换该位置的值。
样例验证
示例 1:位置 ,速度
。按位置排序后速度为
,已非递减,LNDS = 3,答案为
。
示例 2:位置 ,速度
。按位置排序后速度为
,LNDS = 1,答案为
。
复杂度分析
- 时间复杂度:
,排序
加上二分求 LNDS
。
- 空间复杂度:
。
代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n;
scanf("%d", &n);
vector<pair<int,int>> cars(n);
for (int i = 0; i < n; i++) {
scanf("%d%d", &cars[i].first, &cars[i].second);
}
sort(cars.begin(), cars.end());
vector<int> tails;
for (int i = 0; i < n; i++) {
int v = cars[i].second;
auto it = upper_bound(tails.begin(), tails.end(), v);
if (it == tails.end()) {
tails.push_back(v);
} else {
*it = v;
}
}
printf("%d\n", n - (int)tails.size());
return 0;
}
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine().trim());
int[][] cars = new int[n][2];
for (int i = 0; i < n; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
cars[i][0] = Integer.parseInt(st.nextToken());
cars[i][1] = Integer.parseInt(st.nextToken());
}
Arrays.sort(cars, (a, b) -> a[0] - b[0]);
ArrayList<Integer> tails = new ArrayList<>();
for (int i = 0; i < n; i++) {
int v = cars[i][1];
int lo = 0, hi = tails.size();
while (lo < hi) {
int mid = (lo + hi) / 2;
if (tails.get(mid) > v) hi = mid;
else lo = mid + 1;
}
if (lo == tails.size()) tails.add(v);
else tails.set(lo, v);
}
System.out.println(n - tails.size());
}
}

京公网安备 11010502036488号