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!("性能测试结束");
}
执行结果
2023-03-29:如何高效计算三条线路选择方案?小A的旅行线路规划问题的更多相关文章
- Android高效计算——RenderScript(一)
高效计算——RenderScript RenderScript是安卓平台上很受谷歌推荐的一个高效计算平台,它能够自动把计算任务分配到各个可用的计算核心上,包括CPU,GPU以及DSP等,提供十分高效的 ...
- IT从业者不可不知的三条定律
信息技术行业,也就是我们所说的IT行业,有着传统行业所未有的发展速度和模式,当然也有着它独特的发展定律.如果你是从事相关行业,下面讲到的三条定律,不可不知. 摩尔定律 比尔·盖茨曾跟通用公司老板说:如 ...
- Effective Objective-C 2.0 — 第三条:多用字面量语法,少用与之等价的方法
第三条:多用字面量语法,少用与之等价的方法 几个类:NSString NSNumber NSArray NSDictionary 字面量语法是一种语法糖(syntactic sugar) NSS ...
- 计算两条直线的交点(C#)
PS:从其他地方看到的源码是有问题的.下面是修正后的 /// <summary> /// 计算两条直线的交点 /// </summary> /// <param name ...
- qt 原子操作 QAtomicInt(++和--都不是原子操作:都包含三条机器指令)
++和--都不是原子操作:都包含三条机器指令 http://sroply.blog.163.com/blog/static/17092651920106117251539/
- HDU_2039——判断三条边是否能组成三角形
Problem Description 给定三条边,请你判断一下能不能组成一个三角形. Input 输入数据第一行包含一个数M,接下有M行,每行一个实例,包含三个正数A,B,C.其中A,B,C & ...
- Entity Framework Core必须牢记的三条引用三条命令
关于EntityFramework Core有三个重要的引用和三条重要的命令,掌握以这六条,基本用Entity Framework Core就得心应手了. 引用1:Install-PackageMic ...
- Codeforces 707C Pythagorean Triples(构造三条边都为整数的直角三角形)
题目链接:http://codeforces.com/contest/707/problem/C 题目大意:给你一条边,问你能否构造一个包含这条边的直角三角形且该直角三角形三条边都为整数,能则输出另外 ...
- [Swift通天遁地]三、手势与图表-(6)创建包含三条折线的线性图表
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...
- [设计原则与模式] 如何理解TDD的三条规则
cp from : https://blog.csdn.net/ibelieve1974/article/details/54948031 如何理解Bob大叔的TDD三条规则?第一条和第三条讲的是 ...
随机推荐
- idea远程仓库无法更新导致的各种错误,jar包无法下载
直接把下面的配置替换成settings.xml中的内容 <?xml version="1.0" encoding="UTF-8"?> <set ...
- mi-root
1.解锁Bootloder 2.刷开发版系统[xposed最新支持8.1],Android版本相对应,线刷的时候一定要记得只选择"全部删除",不要选择"全部删除并lock ...
- 比 poi导入导出更好用的 EasyExcel使用小结
转载请注明出处: 官方文档: https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read 1.简洁 Java解析.生成E ...
- Less-5 和 Less-6 SQL盲注
判断注入点 测试:http://localhost/sqli-labs-master/Less-5/?id=1a和http://localhost/sqli-labs-master/Less-5/?i ...
- 使用ipmitool配置ipmi(远程控制卡)
使用ipmitool配置ipmi(远程控制卡) 在centos安装OpenIPMI: yum install OpenIPMI OpenIPMI-tools 设置开机启动 chkconfig ipmi ...
- TCP 三次握手,给我长脸了噢
大家好,我是小富~ 个人资源分享网站:FIRE 本文收录在 Springboot-Notebook 面试锦集 前言 之前有个小伙伴在技术交流群里咨询过一个问题,我当时还给提供了点排查思路,是个典型的八 ...
- 了解CSS Module作用域隔离原理
CSS Module出现的背景 我们知道,Javascript发展到现在出现了众多模块化规范,比如AMD.CMD. Common JS.ESModule等,这些模块化规范能够让我们的JS实现作用域隔离 ...
- 【牛客小白月赛69】题解与分析A-F【蛋挞】【玩具】【开题顺序】【旅游】【等腰三角形(easy)】【等腰三角形(hard)】
比赛传送门:https://ac.nowcoder.com/acm/contest/52441 感觉整体难度有点偏大. 作者:Eriktse 简介:19岁,211计算机在读,现役ACM银牌选手力争以通 ...
- ChatGPT3.5使用体验
优点 1.ChatGPT 能颠覆现有的搜索引擎(百度.谷歌). 2.ChatGPT 的交互体验非常好,满足"智能助手"这种工具. 3.如何使用好ChatCPT? 回到一个经典的问题 ...
- Service Mesh之Istio基础入门
技术背景 分布式服务治理 所谓分布式服务治理就是对服务不断增长的复杂度的管控和管理:管控及管理包含网络拓扑变动.网络延时.通信安全.API网关.服务注册和发现.服务熔断容错.服务超时重试.服务部署.数 ...