题目链接

撞车

题目描述

一条单向单车道的道路上有 辆车,第 辆车位于 ,速度大小为 。为了避免碰撞,可以移除一些车辆。请求出至少需要移除几辆车,才能让剩下的车不发生碰撞。

解题思路

两辆车会发生碰撞的充要条件是:位于后方的车辆速度快于前方的车辆。换言之,要使任意两辆车 不发生碰撞,如果车 在车 的后方(即 ),那么必须满足车 的速度不大于车 的速度(即 )。

这个要求启发我们将问题进行转化:

  1. 首先,我们将所有的车辆按照其初始位置 从小到大进行排序。
  2. 排序后,我们就得到了一个按位置排列的车辆序列,以及它们对应的速度序列。
  3. 要使这些车辆不发生碰撞,保留下来的车辆(保持它们原有的相对顺序)的速度必须构成一个不下降的序列。

因此,问题就转化成了:对按位置排序后的速度序列,求解其“最长不下降子序列”(LNDS)。这个子序列的长度,就是我们最多可以保留的、不会发生碰撞的车辆数量。

最终,需要移除的车辆数就等于总车辆数 减去最长不下降子序列的长度。

求解最长不下降子序列的长度,我们可以复用上一题的思路,即采用贪心算法结合二分查找,在 的时间复杂度内解决。

代码

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

struct Car {
    int p, v;
};

// 自定义排序规则,按位置 p 排序
bool compareCars(const Car& a, const Car& b) {
    return a.p < b.p;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int n;
    cin >> n;
    if (n == 0) {
        cout << 0 << "\n";
        return 0;
    }

    vector<Car> cars(n);
    for (int i = 0; i < n; i++) {
        cin >> cars[i].p >> cars[i].v;
    }

    sort(cars.begin(), cars.end(), compareCars);

    // LNDS 求解过程
    vector<int> d;
    for (int i = 0; i < n; i++) {
        int v = cars[i].v;
        if (d.empty() || v >= d.back()) {
            d.push_back(v);
        } else {
            *upper_bound(d.begin(), d.end(), v) = v;
        }
    }

    cout << n - d.size() << "\n";

    return 0;
}
import java.util.Scanner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

class Car {
    int p, v;

    public Car(int p, int v) {
        this.p = p;
        this.v = v;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        if (n == 0) {
            System.out.println(0);
            return;
        }
        Car[] cars = new Car[n];
        for (int i = 0; i < n; i++) {
            cars[i] = new Car(sc.nextInt(), sc.nextInt());
        }

        // 根据位置 p 排序
        Arrays.sort(cars, Comparator.comparingInt(c -> c.p));

        // LNDS 求解过程
        ArrayList<Integer> d = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            int v = cars[i].v;
            if (d.isEmpty() || v >= d.get(d.size() - 1)) {
                d.add(v);
            } else {
                int pos = upperBound(d, v);
                d.set(pos, v);
            }
        }

        System.out.println(n - d.size());
    }

    private static int upperBound(ArrayList<Integer> arr, int key) {
        int low = 0, high = arr.size();
        while (low < high) {
            int mid = low + (high - low) / 2;
            if (arr.get(mid) > key) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
        return low;
    }
}
import bisect

n = int(input())
if n == 0:
    print(0)
else:
    cars = []
    for _ in range(n):
        cars.append(list(map(int, input().split())))
    
    # 根据位置 p 排序
    cars.sort(key=lambda x: x[0])
    
    # 提取速度序列
    velocities = [car[1] for car in cars]
    
    # LNDS 求解过程
    d = []
    for v in velocities:
        if not d or v >= d[-1]:
            d.append(v)
        else:
            # 找到 d 中第一个大于 v 的元素的位置并替换
            pos = bisect.bisect_right(d, v)
            d[pos] = v
            
    print(n - len(d))

算法及复杂度

  • 算法:排序 + 贪心 + 二分查找
  • 时间复杂度:,主要由对车辆按位置排序和求解最长不下降子序列两个部分贡献。
  • 空间复杂度:,用于存储车辆信息和求解LNDS所需的辅助数组。