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!("性能测试结束");
}

执行结果

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

  1. Android高效计算——RenderScript(一)

    高效计算——RenderScript RenderScript是安卓平台上很受谷歌推荐的一个高效计算平台,它能够自动把计算任务分配到各个可用的计算核心上,包括CPU,GPU以及DSP等,提供十分高效的 ...

  2. IT从业者不可不知的三条定律

    信息技术行业,也就是我们所说的IT行业,有着传统行业所未有的发展速度和模式,当然也有着它独特的发展定律.如果你是从事相关行业,下面讲到的三条定律,不可不知. 摩尔定律 比尔·盖茨曾跟通用公司老板说:如 ...

  3. Effective Objective-C 2.0 — 第三条:多用字面量语法,少用与之等价的方法

    第三条:多用字面量语法,少用与之等价的方法 几个类:NSString  NSNumber  NSArray  NSDictionary 字面量语法是一种语法糖(syntactic sugar) NSS ...

  4. 计算两条直线的交点(C#)

    PS:从其他地方看到的源码是有问题的.下面是修正后的 /// <summary> /// 计算两条直线的交点 /// </summary> /// <param name ...

  5. qt 原子操作 QAtomicInt(++和--都不是原子操作:都包含三条机器指令)

    ++和--都不是原子操作:都包含三条机器指令 http://sroply.blog.163.com/blog/static/17092651920106117251539/

  6. HDU_2039——判断三条边是否能组成三角形

    Problem Description 给定三条边,请你判断一下能不能组成一个三角形.   Input 输入数据第一行包含一个数M,接下有M行,每行一个实例,包含三个正数A,B,C.其中A,B,C & ...

  7. Entity Framework Core必须牢记的三条引用三条命令

    关于EntityFramework Core有三个重要的引用和三条重要的命令,掌握以这六条,基本用Entity Framework Core就得心应手了. 引用1:Install-PackageMic ...

  8. Codeforces 707C Pythagorean Triples(构造三条边都为整数的直角三角形)

    题目链接:http://codeforces.com/contest/707/problem/C 题目大意:给你一条边,问你能否构造一个包含这条边的直角三角形且该直角三角形三条边都为整数,能则输出另外 ...

  9. [Swift通天遁地]三、手势与图表-(6)创建包含三条折线的线性图表

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  10. [设计原则与模式] 如何理解TDD的三条规则

    cp from  :  https://blog.csdn.net/ibelieve1974/article/details/54948031 如何理解Bob大叔的TDD三条规则?第一条和第三条讲的是 ...

随机推荐

  1. 搬运 nginx代理https

    oauth2-client在Nginx代理后遇到的问题和解决方案  2020-01-17 2020-05-27  TECH 30 MINUTES READ (ABOUT 4442 WORDS) OAu ...

  2. nvm的下载安装

    nvm下载地址:https://github.com/coreybutler/nvm-windows/releases 下载包,双击安装,选取路径, 注意:如果按默认的,安装在c盘的话,那之后的切换版 ...

  3. 《Zookeeper分布式过程协同技术详解》之简介-分布式与Zookeeper简介

    [常见的分布式架构场景面临的问题]一般在主从架构中,主节点进程负责跟踪从节点的状态和任务的有效性,并分配任务到从节点.而这种架构中必须要解决的几个问题是,主节点崩溃.从节点崩溃.通信故障.主节点崩溃: ...

  4. nat是干什么的,为什么要有nat?以及谈谈ovs里使用ct实现nat功能

    博客竟然不显示更新的时间,只有个发布时间.看起来像2个月没更新一样,其实更新了几行呢.好几个东西想理一下,本来想和周记放一起了,但放一起就没有主题了. 当然一搜也有一些很好的博客,更详细:https: ...

  5. JVM 频繁 FULL GC 快速排查整理

    在分享此案例前,先聊聊哪些场景会导致频繁Full GC: 内存泄漏(代码有问题,对象引用没及时释放,导致对象不能及时回收)死循环大对象程序执行了System.gc() 尤其是大对象,80%以上的情况就 ...

  6. 一个基于GPT模型实现的Git Commit信息自动生成工具

    每次提交代码的时候,你是否有为如何写Commit Message而迟迟按不下提交的时刻呢?然后,死磨硬泡写了一些并提交后,又被review的小伙伴吐槽了呢?相信很多小伙伴有过这样的经历吧? 趁着最近C ...

  7. vue cli3中配置生产环境、开发环境、测试环境

    首先在packjson中配置 "scripts": { "serve": "vue-cli-service serve", //调用开发ap ...

  8. STM32F407 学习 (0) 各种外设功能 (中)

    十.高级定时器 1.重复计数器   如果我们设置重复计数器寄存器 RCR 的值为 N,那么更新事件将在定时器发生 N+1 次上溢或下溢时发生.重复计数器的特性,在控制生成 PWM 信号时很有用. 2. ...

  9. js循环中reduce的用法简单介绍

    reduce() 方法接收一个函数作为累加器,reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值(上一次回调的返回值),当前元素值,当前索引 ...

  10. 相同基准点的多个rvt BIM模型数据配准后位置有错位偏差问题处理

    场景:提供的bim模型数据包含多个rvt格式数据,这些数据具有相同的基准点,如: 在使用ArcGIS Pro处理了其中两份rvt格式数据(建筑和给排水),发布后在前端展示发现数据错位: 红色管线的给排 ...