2023-03-29:如何高效计算三条线路选择方案?小A的旅行线路规划问题

2023-03-29:第一行有一个正整数n(3<=n<=100000),代表小A拟定的路线数量
第二行有n个正整数,第i个代表第i条路线的起始日期
第三行有n个正整数,第i个代表第i条路线的终止日期
输入保证起始日期小于终止日期
日期最小是1,最大不超过1000000000
小A打算选三个路线进行旅游,比如 A -> B -> C
要求A的结束日期要小于B的开始日期,B的结束日期要小于C的开始日期。
输出一个非负整数,代表线路的方案数量。
例子
输入
6
4 1 3 2 1 2
4 1 3 3 2 2
输出
6
解释
[1,1] -> [2,2] -> [3,3]
[1,1] -> [2,2] -> [4,4]
[1,1] -> [2,3] -> [4,4]
[1,2] -> [3,3] -> [4,4]
[1,1] -> [3,3] -> [4,4]
[2,2] -> [3,3] -> [4,4]
来自拼多多。

答案2023-03-29:

方法一: 暴力算法

步骤:

1.找出所有路线的最晚结束日期,记为max。
2.对于所有路线按照起始日期进行排序。
3.使用一个三维数组dp[i][j][k],其中ii表示当前考虑到第ii条路线,jj表示还需要选择jj条路线,kk表示前一条路线的结束日期。
4.递归计算每个状态的方案数。对于当前情况,分为两种可能:
不选当前路线,即dp[i][j][k] = dp[i+1][j][k]。
选择当前路线,即dp[i][j][k] = dp[i][j][k]=dp[i+1][j−1][roads[i][1]],其中roads[i][1]roads[i][1]表示当前路线的结束日期。
5.记忆化搜索,避免重复计算。
6.最终,dp[0][3][0]dp[0][3][0]就是所求的答案。

方法二:线段树算法

步骤:

1.将所有路线按照起始日期排序。
2.构建一个数组sortedsorted,其中包含所有路线的起始日期和结束日期,并将其排序。可以使用一个线段树维护sortedsorted数组的前缀和。
3.使用三个线段树分别统计当前路径长度为1、2、3时的方案数。具体地,在遍历每个路线时,先查询出所有结束日期小于该路线起始日期的路线组合数量,然后将该路线加入到线段树中,并更新线段树的值。
4.最终,三个线段树的总和就是符合条件的路线组合数量。

对比

方法一的时间复杂度为O(n^3)。由于需要递归计算每个状态的方案数,因此当路线数量较多时,时间复杂度会非常高。

方法二的时间复杂度为O(nlogn)。由于使用了线段树优化,可以大大降低时间复杂度。

因此,方法二比方法一更加高效。

rust代码

use std::time::Instant;

fn num1(roads: &mut Vec<Vec<i32>>) -> i32 {
    let n = roads.len();
    let mut max = 0;
    for i in 0..n {
        max = std::cmp::max(max, roads[i][1]);
    }

    //let mut sorted_roads = roads.clone();
    roads.sort_by(|a, b| a[0].cmp(&b[0]));

    let mut dp = vec![vec![vec![-1; (max + 1) as usize]; 4]; n];
    for a in 0..n {
        for b in 0..4 {
            for c in 0..=max {
                dp[a][b][c as usize] = -1;
            }
        }
    }

    process1(&roads, 0, 3, 0, &mut dp)
}

fn process1(
    roads: &Vec<Vec<i32>>,
    i: usize,
    rest: i32,
    end: i32,
    dp: &mut Vec<Vec<Vec<i32>>>,
) -> i32 {
    if rest == 0 {
        return 1;
    }
    if i == roads.len() {
        return 0;
    }
    if dp[i][rest as usize][end as usize] != -1 {
        return dp[i][rest as usize][end as usize];
    }

    let p1 = process1(roads, i + 1, rest, end, dp);
    let p2 = if roads[i][0] > end {
        process1(roads, i + 1, rest - 1, roads[i][1], dp)
    } else {
        0
    };
    let ans = p1 + p2;
    dp[i][rest as usize][end as usize] = ans;
    ans
}

fn num2(roads: &mut Vec<Vec<i32>>) -> i32 {
    let n = roads.len();
    let mut sorted = vec![0; n << 1];
    for i in 0..n {
        sorted[i << 1] = roads[i][0];
        sorted[i << 1 | 1] = roads[i][1];
    }
    sorted.sort_unstable(); // 使用 sort_unstable() 函数排序
    roads.sort_unstable_by(|a, b| a[0].cmp(&b[0])); // 使用 sort_unstable_by() 函数排序
    let mut it1 = IndexTree::new((n as i32) << 1);
    let mut it2 = IndexTree::new((n as i32) << 1);
    let mut it3 = IndexTree::new((n as i32) << 1);
    for road in roads {
        let l = rank(&sorted, road[0]);
        let r = rank(&sorted, road[1]);
        it1.add(r, 1);
        it2.add(r, it1.sum(l - 1));
        it3.add(r, it2.sum(l - 1));
    }
    it3.sum((n as i32) << 1)
}

fn rank(sorted: &Vec<i32>, num: i32) -> i32 {
    let mut l = 0;
    let mut r = sorted.len() as i32 - 1;
    let mut m = 0;
    let mut ans = 0;
    while l <= r {
        m = (l + r) / 2;
        if sorted[m as usize] >= num {
            ans = m;
            r = m - 1;
        } else {
            l = m + 1;
        }
    }
    ans + 1
}

struct IndexTree {
    tree: Vec<i32>,
    n: i32,
}

impl IndexTree {
    fn new(size: i32) -> Self {
        Self {
            tree: vec![0; (size + 1) as usize],
            n: size,
        }
    }

    fn sum(&self, mut index: i32) -> i32 {
        let mut ret = 0;
        while index > 0 {
            ret += self.tree[index as usize];
            index -= (index & -index);
        }
        ret
    }

    fn add(&mut self, mut index: i32, d: i32) {
        while index <= self.n {
            self.tree[index as usize] += d;
            index += (index & -index);
        }
    }
}

fn random_roads(n: usize, v: i32) -> Vec<Vec<i32>> {
    let mut roads = vec![vec![0, 0]; n];
    for i in 0..n {
        let mut a = rand::random::<i32>() % v + 1;
        if a <= 0 {
            a += v;
        }
        let mut b = rand::random::<i32>() % v + 1;
        if b <= 0 {
            b += v;
        }
        let (start, mut end) = if a < b { (a, b) } else { (b, a) };
        if start == end {
            end += 1;
        }
        roads[i][0] = start;
        roads[i][1] = end;
    }
    roads
}

fn main() {
    const N: usize = 50;
    const V: i32 = 50;
    const TEST_TIME: usize = 5000;

    println!("功能测试开始");
    for i in 0..TEST_TIME {
        let n = rand::random::<usize>() % N + 1;
        let mut roads = random_roads(n, V);
        let mut roads2 = roads.clone();
        let roads3 = roads2.clone();
        let ans1 = num1(&mut roads);
        let ans2 = num2(&mut roads2);
        if ans1 != ans2 {
            println!("出错了!{}", i);
            println!("出错了!ans1 = {}", ans1);
            println!("出错了!ans2 = {}", ans2);
            println!("roads3 = {:?}", roads3);
            return;
        }
    }
    println!("功能测试结束");

    println!("性能测试开始");
    let n = 5000;
    let v = 1000000000;
    let mut roads = random_roads(n, v);
    println!("数组长度 : {}", n);
    println!("数值范围 : {}", v);
    let start = Instant::now();
    num2(&mut roads);
    let end = Instant::now();
    println!("运行时间 : {} 毫秒", (end - start).as_millis());
    println!("性能测试结束");
}

执行结果

在这里插入图片描述

本作品采用《CC 协议》,转载必须注明作者和本文链接
微信公众号:福大大架构师每日一题。最新面试题,涉及golang,rust,mysql,redis,云原生,算法,分布式,网络,操作系统。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
470
粉丝
21
喜欢
37
收藏
22
排名:457
访问:1.9 万
私信
所有博文
社区赞助商