撞车

[题目链接](https://www.nowcoder.com/practice/d8f779b417094dc1a7864712bb9b4c63)

思路

一条单向单车道上有 辆车,第 辆车位于 ,速度为 。求至少移除几辆车,才能使剩余车辆不发生碰撞。

碰撞条件分析

考虑两辆车 (位置 )和 (位置 ),其中 。在时刻 它们的位置分别为 。碰撞发生当且仅当存在 使得:

$$

解得 。由于 ,所以 当且仅当 ,即后车速度大于前车速度。

转化为最长非递减子序列

因此,要使所有剩余车辆不发生碰撞,按位置排序后,速度必须非递减。这等价于:

  1. 将所有车按位置 从小到大排序;
  2. 在速度序列上求最长非递减子序列(LNDS)的长度
  3. 答案为

求 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());
    }
}