CSP历年复赛题-P9751 [CSP-J 2023] 旅游巴士

news/2024/9/26 2:24:57

原题链接:https://www.luogu.com.cn/problem/P9751

题意解读:

  在有向图中(每条边的权值是可通过的最早时间,通过每条边所用的时间是1,也可以认为每条边的路径长度是1),在某个k的整数倍时间点start出发,从1号点出发,计算到达n点的最短路径dist,使得dist%k==0(因为从起点出发和终点发车的时间都是k的整数倍,路径长度也一定是k的整数倍),计算到达终点的时间end=start+dist的最小值。

样例模拟:

关于本题的“骗分”方案,请移步之前的文章CSP-J 2023 T4 旅游巴士(CSP-J考纲范围内的解法:BFS+二分),本文主要针对正解用容易理解的邻接表重新写一下。

解题思路:

  如果想到了最短路,每条边路径长度相同,那么首先会想到BFS,正如[CSP-J2019] 加工零件中所介绍过的,在用BFS计算起点到终点的普通最短路径时,如果终点只能走一次,那么最短路径长度也是唯一的,但如果每个点可以不止走一次,那么到终点的路径也会有多种。

  本题要求取最短的那一个路径dist,使得dist%k==0,因为普通最短路不一定能整除k,所以计算起点到终点的路径时要把%k的所有路径长度都存储下来,对于一个节点是否能重复走,需要依据路径长度%k是否已经走到过。通过一个二维数组dist[N][K]即可搞定。

  由于每个节点都有一个最早能通过的时间,在BFS过程中,下一个节点能不能走,除了根据路径长度%k是否已经存在,还要判断当前的时间是否>=节点最早能通过时间,当前的时间就是出发时间+已经走过的路径。

  下面梳理一下截止目前的解题流程:

1、枚举出发时间点start:0, 3, 6 ......  直到最大的边权值(最早出发时间)

2、BFS计算起点到终点最短的模k为0的路径dist

3、如果路径存在,则更新到终点时间的最小值,ans = min(ans, start+dist)

4、如果路径不存在,输出-1

  分析一下时间复杂度,出发时间最大值是10^6,节点和边数10^4,总的时间复杂度在10^10/3,约是O(10^9)规模

  如何优化?BFS不能省,那么枚举出发时间点是否可以更快?想到了二分,下面分析一下是否具备单调性:

如果出发时间越大,显然受限制的节点越少,能走出一个最短路模k为0的路径可能性越高。

所以可以针对出发时间进行二分,找一个最小的出发时间,使得能走出最短路模k为0,然后更新到达终点时间的最小值。

但是,二分起点得到的终点并不是单调的,起点大肯定能走出模k为0的最短路,但起点最小不代表到达终点时间的时间也最小!

因此,更合理的方法是二分到达终点的最小时间,反向建图,bfs得到一个到起点的模k为0的时间。

100分代码:

#include <bits/stdc++.h>
using namespace std;const int N = 10005, K = 105;struct gnode
{int v; //边的终点int a; //边的权值,最早可通过时间
};
vector<gnode> g[N];
int dist[N][K]; //dist[i][j]表示从起点到i的最短的%k=j的路径
int n, m, k, ans = INT_MAX;struct qnode
{int u; //节点int d; //u节点距离起点路径长度
};//终点从end开始,通过bfs能否找到到起点时间%k=0的路径
bool bfs(int end)
{memset(dist, -1, sizeof(dist));queue<qnode> q;q.push({n, end});dist[n][0] = end;while(q.size()){qnode from = q.front(); q.pop();int u = from.u;for(gnode to : g[u]){int v = to.v;int d = from.d - 1; //到下一个点的时间是上一个点的时间-1if(dist[v][d % k] == -1 && d >= to.a) //如果到v的时间%k没有到过,且时间允许{q.push({v, d});dist[v][d % k] = d;}}}return dist[1][0] != -1;
}int main()
{cin >> n >> m >> k;int u, v, a;while(m--){cin >> u >> v >> a;g[v].push_back({u, a}); //反向建图}int l = 1, r = 2000000, ans = -1; //r取值2000000防止k*mid超intwhile(l <= r){int mid = (l + r) >> 1; //二分到达终点时间k*midif(bfs(k * mid)) ans = k * mid, r = mid - 1;else l = mid + 1;}cout << ans;return 0;
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

Jmeter中P函数使用tips

如上图中的${__P(login_token)}若要能够被正常使用,需要在该线程组之前增加调试取样器,同时在调试取样器的名称中定义setProperty: 若为了美观而想在调试取样器中的注释中定义setProperty,则必须加入调试后置程序,否则无法调用:

Async 注解底层异步线程

一、前言 开发中我们经常会用到异步方法调用,具体到代码层面,异步方法调用的实现方式有很多种,比如最原始的通过实现 Runnable 接口或者继承 Thread 类创建异步线程,然后启动异步线程;再如,可以直接用 java.util.concurrent 包提供的线程池相关 API 实现异步方法调用。 如…

dedecms图集dede:field name=imgurls不能二次使用的解决办法

{dede:field name=imgurls alt=图片输出区}图片链接 [field:linkurl/]图片地址 [field:imgsrc/]{/dede:field}这个标签不能同时使用2次,所以第二次的话用!!!{dede:productimagelist}图片链接 [field:linkurl/]图片地址 [field:imgsrc/]{/dede:productimagelist}<div id=&…

IntelliJ IDEA 2024 mac/win版:编程利器,智慧之选

IntelliJ IDEA 2024是一款由JetBrains精心打造的集成开发环境(IDE),专为Java等编程语言量身打造,同时支持多种其他语言,为开发者提供了卓越的开发体验。 IntelliJ IDEA 2024 mac/win版获取这款IDE凭借其出色的智能化和高效性,赢得了广大开发者的喜爱。IDEA 2024不仅提供了丰…

JAX-中文文档-八-

JAX 中文文档(八)原文:jax.readthedocs.io/en/latest/自动微分手册原文:jax.readthedocs.io/en/latest/notebooks/autodiff_cookbook.htmlalexbw@, mattjj@ JAX 拥有非常通用的自动微分系统。在这本手册中,我们将介绍许多巧妙的自动微分思想,您可以根据自己的工作进行选择…

KVM虚拟机安装部署全攻略 cockpit

01 原理KVM(Kernel-based Virtual Machine)虚拟化技术是一种基于内核的虚拟化技术,KVM虚拟化技术的实现依赖于CPU的虚拟化扩展(如Intel VT和AMD-V)。当宿主机启动时,KVM会加载一个轻量级的内核模块kvm.ko,该模块负责与硬件进行交互,实现虚拟机的创建、管理和调度。02 组…

DC/AC电源模块:提高太阳能发电系统的效率和稳定性

BOSHIDA DC/AC电源模块:提高太阳能发电系统的效率和稳定性 DC/AC电源模块是太阳能发电系统中的一个重要组成部分,其作用是将太阳能转化为交流电以供家庭或工业使用。它可以提高太阳能发电系统的效率和稳定性,使得太阳能发电系统更加可靠和持久。 一,DC/AC电源模块可以提高…

synchronized 和 ReentrantLock (Lock)区别,优劣对比

synchronized 和 ReentrantLock (Lock)区别两种方法都是为了确保多线程环境中的线程安全,但它们使用了不同的同步机制:synchronized 关键字和 Lock 接口。下面详细对比这两种方法的区别、优缺点以及适用场景。 synchronized 关键字 public synchronized void addSession(Http…