信息学奥赛一本通 2101:【23CSPJ普及组】旅游巴士(bus) | 洛谷 P9751 [CSP-J 2023] 旅游巴士

【题目链接】

ybt 2101:【23CSPJ普及组】旅游巴士(bus)
洛谷 P9751 [CSP-J 2023] 旅游巴士

【题目考点】

1. 图论:求最短路Dijkstra, SPFA
2. 动态规划
3. 二分答案
4. 图论:广搜BFS

【解题思路】

解法1:Dijkstra堆优化

每个地点是一个顶点,每条道路是一条边,道路只能单向通行,该图是有向图。通过每条边用时都是1单位时间,那么该图是无权图。每条道路都有开放时刻a,也就是说对于每条边:该边的a时刻后,才存在,在a时刻前不存在。
该题问的是离开景区的最早时间,也就是到达顶点n的最早时间。
如果给定进入园区的时刻,即便每条边有开放时间的限制,我们依然可以通过求最短路算法(如BFS,Dijkstra,SPFA)得到从起点到终点的最短路径长度。但到达终点的时刻未必是k的倍数。
观察变量取值范围,看到 1 ≤ k ≤ 100 1\le k \le 100 1k100,这是本题的突破点。
要想使到达顶点n的时刻是k的倍数,那么到达前一个顶点的时刻一定应该满足模k等于k-1(除以k的余数是k-1),到达路径上再前一个顶点的时刻应该满足模k等于k-2。。。
因此到达某顶点的时刻模k的值也是我们的需要考虑的限制。

状态定义:
  • 阶段:到达顶点i,到达该顶点的时间模k的值
  • 决策:下一步走到哪个邻接点
  • 策略:路径
  • 策略集合:在k的整数倍时刻从起点出发到达顶点i的所有路径
  • 条件:时刻最早
  • 统计量:时刻

d p [ i ] [ j ] dp[i][j] dp[i][j]:在k的整数倍时刻从起点出发到达顶点i,到达顶点i的时刻模k等于j的最早时刻。

状态转移方程:
  • 策略集合:在k的整数倍时刻从起点出发到达顶点i的所有路径
  • 分割策略集合:根据到达顶点v的前一个顶点的情况分割策略集合

由于本题没有说明整个图是有向无环图,因此不能使用拓扑排序的方法。
每个状态都可能多次被更新,因此需要使用Dijkstra或SPFA算法,更新状态变量。
可以从顶点u走到下一个顶点v时,更新顶点v的状态。
假设在第0时刻从顶点出发,在 t t t时刻到达顶点u,边<u,v>的开放时间为 a a a

  • 如果 t ≥ a t\ge a ta,通过顶点u到达顶点v的最早时刻 t m i n = t + 1 tmin = t+1 tmin=t+1
  • 如果 t < a t<a t<a,为了从顶点u通过边<u,v>走到顶点v,需要在入园前等待一段时间后再入园,入园时刻必须是 k k k的整数倍。(这个人在游玩时不能在任何位置停留,那么只能在入园前等待)
    如果等待 k k k时间后再入园,走相同路径到达顶点u时的时刻就是 t + k t+k t+k
    如果等待 2 k 2k 2k时间后再入园,走相同路径到达顶点u时的时刻就是 t + 2 k t+2k t+2k
    如果等待 x k xk xk时间后再入园,走相同路径到达顶点u时的时刻就是 t + x k t+xk t+xk。希望到达顶点u时边<u,v>已开放,则需要满足不等式
    t + x k ≥ a t+xk\ge a t+xka,即 x ≥ a − t k x \ge \frac{a-t}{k} xkat x x x最小为 ⌈ a − t k ⌉ \lceil \frac{a-t}{k} \rceil kat
    因此如果想要通过<u,v>到达顶点v,那么到达顶点u的最早时刻为 t + ⌈ a − t k ⌉ k t+\lceil \frac{a-t}{k} \rceil k t+katk
    因此如果 t < a t<a t<a,通过顶点u到达顶点v的最早时刻为 t + ⌈ a − t k ⌉ k + 1 t+\lceil \frac{a-t}{k} \rceil k+1 t+katk+1

因此存在一条到达顶点v的最早到达时刻为 t m i n tmin tmin的路径,使用 t m i n tmin tmin更新 d p [ v ] [ t m i n % k ] dp[v][tmin\%k] dp[v][tmin%k]。即如果 d p [ v ] [ t m i n % k ] > t m i n dp[v][tmin\%k]>tmin dp[v][tmin%k]>tmin,那么设 d p [ v ] [ t m i n % k ] = t m i n dp[v][tmin\%k]=tmin dp[v][tmin%k]=tmin

具体实现

如果到达某个顶点的最早时刻发生更新,而后应该再更新到达其邻接点的最早时刻。
该过程可以使用Dijkstra堆优化算法完成,需要把达顶点v的最早到达时刻为 t m i n tmin tmin的路径加入到优先队列。
到达顶点n,最早到达时刻模k等于0的最早到达时刻 d p [ n ] [ 0 ] dp[n][0] dp[n][0]就是最终结果。

复杂度分析

每个顶点有k个状态,因此可以将每个顶点看作k个顶点,原来的每条边可以看作k条边,整个图变为有nk个顶点,mk条边的图。
已知Dijkstra堆优化的时间复杂度为 O ( m k ⋅ l o g ( m k ) ) O(mk\cdot log(mk)) O(mklog(mk)),m最大是 2 ∗ 1 0 4 2*10^4 2104,k最大是100,该复杂度可以接受。

解法2:二分答案+bfs

可以反向思考,先通过二分答案确定到达终点n的时刻 t e te te,保证te是k的整数倍。
由于 a ≤ 1 0 6 a\le 10^6 a106,当开始时刻达到 1 0 6 10^6 106时,图中所有边都开放了。
边数 m ≤ 2 ∗ 1 0 4 m\le 2*10^4 m2104,假设在大约 1 0 6 10^6 106时刻出发,从顶点1到顶点n的路径需要经过所有的边,到达时刻不超过 1 0 7 10^7 107
由于k最大为100,我们可以二分到达时刻除以k的值,该值最小为0,最大值设大一点,就写 1 0 7 10^7 107,这样求出的 t e te te最小为0,最大不超过 1 0 9 10^9 109
判断解 t e te te是否满足条件:
建立原图的反图。从顶点n开始进行广搜。
如果搜索到顶点u时的时刻为t,u有邻接点v,在原图中,就是看在t-1时刻边<v,u>是否已开放,已知<v,u>的开放时间是a,如果 t − 1 ≥ a t-1\ge a t1a,那么可以在原图中从顶点v经过<v,u>到达顶点u。
在反图中,也就是判断如果 t − 1 ≥ a t-1\ge a t1a,那么边<u,v>已开放,可以在反图中从顶点u访问到邻接点v。
看是否存在到达顶点1的,到达时刻是k的整数倍的路径。
设vis数组,需要同时考虑到达顶点i,以及到达该顶点的时刻模k的值。
vis[i][j]表示到达顶点i,且到达时刻模k等于j的情况是否已发生。

因为在该问题的广搜的过程中,到达顶点的时刻是不断减少的,如果已经发生过到达顶点i且时刻模k等于j的情况,设该时刻是 t 1 t_1 t1。再次发生到达顶点i且时刻模k等于j时的时刻是 t 2 t_2 t2,则一定有 t 1 > t 2 t_1>t_2 t1>t2
如果从顶点i,时刻 t 1 t_1 t1出发通过一条路径到达顶点1,到达顶点1的时刻模k不等于0。那么从顶点i,时刻 t 2 t_2 t2出发经过相同的路径到达顶点1,到达顶点1的时刻模k的值和上面的情况中的值一定是一样的,也不为0。
因此如果已经发生过到达顶点i,到达时刻模k等于j的情况,就不用考虑后面出现的到达顶点i,到达时刻模k等于j的情况。

在反图进行广搜的过程中,对于每个出队的顶点u及到达u的时刻t
遍历u的邻接点v,到达顶点v的时刻为 t − 1 t-1 t1,<u,v>的开放时间为a

  • 如果 t − 1 < a t-1<a t1<a,则在原图中到达v时<v,u>边未开放,略过这种情况。
  • 如果到达顶点v,到达时刻模k等于 ( t − 1 ) % k (t-1)\%k (t1)%k的情况已经发生过,则略过。

不满足以上情况,才要访问顶点v:
如果顶点v就是顶点1,且到达的时刻 t − 1 t-1 t1是k的倍数,则找到了一个可行的解,答案出园时刻 t e te te满足条件。
而后存在到达顶点v,时刻为 t − 1 t-1 t1的情况,将该情况加入队列。并标记到达顶点v,时刻模k等于 ( t − 1 ) % k (t-1)\%k (t1)%k的情况已存在。
如果广搜结束后也没有找到可行的解,则出园时刻 t e te te不满足条件,返回假。

【题解代码】

解法1:Dijkstra堆优化算法+动态规划

#include<bits/stdc++.h>
using namespace std;
const int N = 10005, K = 100, INF = 0x3f3f3f3f;
struct Path
{int u, t;//存在到达顶点u,时刻为t的路径 bool operator < (const Path &b) const{return b.t < t;//时刻t更小更优先 }
};
struct Edge
{int v, a;
};
int n, m, k, dp[N][K];//在k的整数倍时刻从起点出发到达顶点i,到达顶点i的时刻模k等于j的最早时刻。
vector<Edge> edge[N];
int divCeil(int a, int b)//ceil(a/b)
{return (a-1)/b+1; 
} 
void dijkstra()
{priority_queue<Path> pq;memset(dp, 0x3f, sizeof(dp));//求最短路径,先将状态设为无穷 dp[1][0] = 0;//到达顶点1(起点),到达时刻模k为0的最早时刻为0pq.push(Path{1, 0});while(!pq.empty()){int u = pq.top().u, t = pq.top().t;pq.pop();for(Edge e : edge[u]){int v = e.v, a = e.a, tmin;//tmin:想要从u通过<u,v>,到达v的最早时刻 if(t >= a)tmin = t+1;elsetmin = t+1+divCeil(a-t, k)*k;if(dp[v][tmin%k] > tmin){dp[v][tmin%k] = tmin;pq.push(Path{v, tmin});}}}
}
int main()
{int u, v, a;cin >> n >> m >> k;for(int i = 1; i <= m; ++i){cin >> u >> v >> a;edge[u].push_back(Edge{v, a});}dijkstra();cout << (dp[n][0] == INF ? -1 : dp[n][0]);return 0;
}
解法2:二分答案+bfs
#include<bits/stdc++.h>
using namespace std;
const int N = 10005, K = 100, INF = 0x3f3f3f3f;
struct Path
{int u, t;//到达顶点u,到达时刻t 
};
struct Edge
{int v, a;
};
int n, m, k;
vector<Edge> rg[N];//反图 
bool vis[N][K];
bool check(int te)//判断到达时刻为te时,是否存在从n到1的,到达顶点1的时刻模k等于0的路径。 
{memset(vis, 0, sizeof(vis));queue<Path> que;vis[n][te%k] = true;que.push(Path{n, te});while(!que.empty()){int u = que.front().u, t = que.front().t;que.pop();for(Edge e : rg[u]){int v = e.v, a = e.a;if(t-1 >= a && !vis[v][(t-1)%k])//到顶点v时<v,u>已开放,同时现在没有在访问时刻模k等于(t-1)%k时访问顶点v {if(v == 1 && (t-1)%k == 0)//找到解,te满足条件 return true;vis[v][(t-1)%k] = true;que.push(Path{v, t-1});}}}return false;//te不满足条件 
}
int main()
{int u, v, a;cin >> n >> m >> k;for(int i = 1; i <= m; ++i){cin >> u >> v >> a;rg[v].push_back(Edge{u, a});//建反图}int l = 0, r = 1e7; while(l < r){int mid = (l+r)/2;if(check(mid*k))//答案是mid*k,mid*k最大为1e9r = mid;elsel = mid+1; }if(check(l*k))//二分得到的答案也未必是满足条件的,需要再判断一下 cout << l*k;elsecout << -1;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/14211.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Matplotlib基础01( 基本绘图函数/多图布局/图形嵌套/绘图属性)

Matplotlib基础 Matplotlib是一个用于绘制静态、动态和交互式图表的Python库&#xff0c;广泛应用于数据可视化领域。它是Python中最常用的绘图库之一&#xff0c;提供了多种功能&#xff0c;可以生成高质量的图表。 Matplotlib是数据分析、机器学习等领域数据可视化的重要工…

Nginx 配置 SSL(HTTPS)详解

Nginx作为一款高性能的HTTP和反向代理服务器&#xff0c;自然支持SSL/TLS加密通信。本文将详细介绍如何在Nginx中配置SSL&#xff0c;实现HTTPS的访问。 随着互联网安全性的日益重要&#xff0c;HTTPS协议逐渐成为网站加密通信的标配。Nginx作为一款高性能的HTTP和反向代理服务…

Mybatis篇

1&#xff0c;什么是Mybatis &#xff08; 1 &#xff09;Mybatis 是一个半 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它内部封装了 JDBC&#xff0c;开发时只需要关注 SQL 语句本身&#xff0c;不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁…

文件上传全详解

前言 我们下面进行下一个漏洞——文件上传的学习。文件上传是常见漏洞之一&#xff0c;是Web安全必学漏洞。为探讨清楚文件上传漏洞的诸多细节&#xff0c;我们特以经典的upload-labs进行从入门到进阶的专项训练。 作者进行upload-labs靶场练习时&#xff0c;在环境配置上出了…

【centOS】搭建公司内网git环境-GitLab 社区版(GitLab CE)

1. 安装必要的依赖 以 CentOS 7 系统为例&#xff0c;安装必要的依赖包&#xff1a; sudo yum install -y curl policycoreutils openssh-server openssh-clients postfix sudo systemctl start postfix sudo systemctl enable postfix2. 添加 GitLab 仓库 curl -sS https:/…

阿里云 | DeepSeek人工智能大模型安装部署

ModelScope是阿里云人工智能大模型开源社区 ModelScope网络链接地址 https://www.modelscope.cn DeepSeek模型库网络链接地址 https://www.modelscope.cn/organization/deepseek-ai 如上所示&#xff0c;在阿里云人工智能大模型开源社区ModelScope中&#xff0c;使用阿里云…

Android13-系统服务大管家-ServiceManager进程-启动篇

文章目录 关注 ServiceMager 原因ServerManager需要掌握的知识资料参考ServiceManager 进程启动启动脚本涉及到的相关源码文件源码跟踪ServiceManager脚本启动位置ServiceManager关联脚本 Native层源码分析main.cpp流程打开驱动 initWithDriverinitmakeProcessState 构造方法op…

【华为OD-E卷 - 113 跳格子2 100分(python、java、c++、js、c)】

【华为OD-E卷 - 跳格子2 100分&#xff08;python、java、c、js、c&#xff09;】 题目 小明和朋友玩跳格子游戏&#xff0c;有 n 个连续格子组成的圆圈&#xff0c;每个格子有不同的分数&#xff0c;小朋友可以选择以任意格子起跳&#xff0c;但是不能跳连续的格子&#xff…

DeepSeek-R1 云环境搭建部署流程

DeepSeek横空出世&#xff0c;在国际AI圈备受关注&#xff0c;作为个人开发者&#xff0c;AI的应用可以有效地提高个人开发效率。除此之外&#xff0c;DeepSeek的思考过程、思考能力是开放的&#xff0c;这对我们对结果调优有很好的帮助效果。 DeepSeek是一个基于人工智能技术…

npm安装electron安装报错

npm安装electron巨慢&#xff0c;报错&#xff0c;换了镜像源也不好使&#xff0c;一般都是网络超时导致的。 cmd窗口执行&#xff1a;&#xff08;打开npm的配置文件&#xff09; npm config edit在配置文件中粘贴&#xff0c;并保存&#xff1a; registryhttps://regis…

x64、aarch64、arm与RISC-V64:详解四种处理器架构

x64、aarch64、arm与RISC-V64:详解四种处理器架构 x64架构aarch64架构ARM架构RISC-V64架构总结与展望在计算机科学领域,处理器架构是构建计算机系统的基石,它决定了计算机如何执行指令、管理内存和处理数据。x64、aarch64、arm与RISC-V64是当前主流的四种处理器架构,它们在…

项目顺利交付,几个关键阶段

年前离放假还有10天的时候&#xff0c;来了一个应急项目&#xff0c; 需要在放假前一天完成一个演示版本的项目&#xff0c;过年期间给甲方领导看。 本想的最后几天摸摸鱼&#xff0c;这么一来&#xff0c;非但摸鱼不了&#xff0c;还得加班。 还在虽然累&#xff0c;但也是…

昇思打卡营第五期(MindNLP特辑)番外:硅基流动 x 华为云DeepSeek V3 API推理MindTinyRAG

1.前言 前脚&#xff0c;DeepSeek面临的巨头企业官宣加入vs多国政府下场质疑的冰火两重天局势尚未平静&#xff08;DeepSeek在美两重天&#xff1a;五大巨头接入&#xff0c;政府诚惶诚恐&#xff09;&#xff1b;后脚&#xff0c;OpenAI被逼急&#xff0c;凌晨亮出全新推理…

MYSQL索引与视图

一、新建数据库 mysql> create database mydb15_indexstu; mysql> use mydb15_indexstu; 二、新建表 &#xff08;1&#xff09;学生表Student mysql> create table Student(-> Sno int primary key auto_increment,-> Sname varchar(30) not null unique,-…

使用java代码操作rabbitMQ收发消息

SpringAMQP 将来我们开发业务功能的时候&#xff0c;肯定不会在控制台收发消息&#xff0c;而是应该基于编程的方式。由于RabbitMQ采用了AMQP协议&#xff0c;因此它具备跨语言的特性。任何语言只要遵循AMQP协议收发消息&#xff0c;都可以与RabbitMQ交互。并且RabbitMQ官方也…

介绍10个比较优秀好用的Qt相关的开源库

记录下比较好用的一些开源库 1. Qt中的日志库“log4qt” log4qt 是一个基于 Apache Log4j 设计理念的 Qt 日志记录库&#xff0c;它为 Qt 应用程序提供了强大而灵活的日志记录功能。Log4j 是 Java 领域广泛使用的日志框架&#xff0c;log4qt 借鉴了其优秀的设计思想&#xff…

【远程控制】安装虚拟显示器

todesk远程发现没显示器的机器有问题 电脑如果不外接一个显示器那么会默认为1024 768 分辨率需要安装虚拟显示器参考 竟然是一个隐私屏幕的解决方案。 虚拟显示器 Parsec-vdd 项目地址 Parsec-vdd 最大的优点是&#xff1a;支持 4K 高刷、可添加多个虚拟屏、 H-Cursor&#…

嵌入式面试题 C/C++常见面试题整理_7

一.什么函数不能声明为虚函数? 常见的不能声明为虚函数的有:普通函数(非成员函数):静态成员函数;内联成员函数;构造函数;友元函数。 1.为什么C不支持普通函数为虚函数?普通函数(非成员函数)只能被overload&#xff0c;不能被override&#xff0c;声明为虚函数也没有什么意思…

赛博算命之 ”梅花易数“ 的 “JAVA“ 实现 ——从玄学到科学的探索

hello~朋友们&#xff01;好久不见&#xff01; 今天给大家带来赛博算命第三期——梅花易数的java实现 赛博算命系列文章&#xff1a; 周易六十四卦 掐指一算——小六壬 更多优质文章&#xff1a;个人主页 JAVA系列&#xff1a;JAVA 大佬们互三哦~互三必回&#xff01;&#xf…

UNI-MOL: A UNIVERSAL 3D MOLECULAR REPRESENTATION LEARNING FRAMEWORK

UNI-MOL: A UNIVERSAL 3D MOLECULAR REPRESENTATION LEARNING FRAMEWORK Neurips23 推荐指数&#xff1a;#paper/⭐⭐⭐#​&#xff08;工作量不小) 动机 在大多数分子表征学习方法中&#xff0c;分子被视为 1D 顺序标记或2D 拓扑图&#xff0c;这限制了它们为下游任务整合…