CSP-S 2024 提高级 第一轮(初赛) 完善程序(2)

【题目】

CSP-S 2024 提高级 第一轮(初赛) 完善程序(2)
(2)(次短路)已知一个n个点m条边的有向图G,并且给定图中的两个点s和t,求次短路(长度严格大于最短路的最短路径)。如果不存在,输出一行“-1”。如果存在,输出两行,第一行表示次短路的长度,第二行表示次短路的一个方案。

#include <cstdio>
#include <queue>
#include <utility>
#include <cstring>
using namespace std;
const int maxn = 2e5 + 10, maxm = 1e6 + 10, inf = 522133279;
int n, m, s, t;
int head[maxn], nxt[maxm], to[maxm], w[maxm], tot = 1;
int dis[maxn << 1], *dis2;
int pre[maxn << 1], *pre2;
bool vis[maxn << 1];
void add(int a, int b, int c) {++tot;nxt[tot] = head[a];to[tot] = b;w[tot] = c;head[a] = tot;
}
bool upd(int a, int b, int d, priority_queue<pair<int, int> > &q) {if (d >= dis[b])return false;if (b < n) ___(1)___;q.push(___(2)___);dis[b] = d;pre[b] = a;return true;
}
void solve() {priority_queue<pair<int, int> >q;q.push(make_pair(0, s));memset(dis, ___(3)___, sizeof(dis));memset(pre, -1, sizeof(pre));dis2 = dis + n;pre2 = pre + n;dis[s] = 0;while (!q.empty()) {int aa = q.top().second; q.pop();if (vis[aa])continue;vis[aa] = true;int a = aa % n;for (int e = head[a]; e ; e = nxt[e]) {int b = to[e], c = w[e];if (aa < n) {if (!upd(a, b, dis[a] + c, q))___(4)___ } else {upd(n + a, n + b, dis2[a] + c, q);}}}
}
void out(int a) {if (a != s) {if (a < n) out(pre[a]);else out(___(5)___);}printf("%d%c", a % n + 1, " \n"[a == n + t]);
}
int main() {scanf("%d%d%d%d", &n, &m,&s,&t);s--, t--;for (int i = 0; i < m; ++i) {int a, b, c;scanf("%d%d%d", &a, &b, &c);add(a - 1, b - 1, c);}solve();if (dis2[t] == inf) puts("-1");else {printf("%d\n", dis2[t]);out(n + t);}return 0;
}
  1. (1)处应填( )
    A.udp(pre[b],n+b,dis[b],q)
    B.upd(a,n+b,d,q)
    C.upd(pre[b],b,dis[b],q)
    D.upd(a,b,d,q)
  2. (2)处应填( )
    A.make_pair(-d,b)
    B.make_pair(d,b)
    C.make_pair(b,d)
    D.make_pair(-b,d)
  3. (3)处应填( )
    A.0xff
    B.0x1f
    C.0x3f
    D.0x7f
  4. (4)处应填( )
    A.upd(a,n+b,dis[a]+c,q)
    B.upd(n+a,n+b,dis2[a]+c.q)
    C.upd(n+a,b,dis2[a]+c,q)
    D.upd(a,b,dis[a]+c,q)
  5. (5)处应填( )
    A.pre2[a%n]
    B.pre[a%n]
    C.pre2[a]
    D.pre[a%n]+1

【题目考点】

1. 图论:次短路径
2. 图论:最短路 Dijkstra堆优化

【解题思路】

int main() {scanf("%d%d%d%d", &n, &m,&s,&t);s--, t--;for (int i = 0; i < m; ++i) {int a, b, c;scanf("%d%d%d", &a, &b, &c);add(a - 1, b - 1, c);}solve();if (dis2[t] == inf) puts("-1");else {printf("%d\n", dis2[t]);out(n + t);}return 0;
}

输入顶点数n,边数m,起点s,终点t
s和t都减1,以及下面添加边时传入a-1, b-1,表示该题输入时顶点编号为1~n,而代码内顶点编号从0~n-1。
循环m次,每次循环输入一条边,表示存在有向边<a, b>,权值为c。

void add(int a, int b, int c) {++tot;nxt[tot] = head[a];to[tot] = b;w[tot] = c;head[a] = tot;
}

add函数的链式前向星实现邻接表的常规操作,tot为链表中用到的最大结点地址,nxt[i]表示地址为i的结点的下一个结点的地址,to[i]表示地址为i的结点上的顶点编号,w[i]表示地址为i的结点上的边的权值。head[a]表示顶点a后面的单链表中第一个结点的地址。
主函数中线调用solve函数,而后如果dist2[t]为inf,也就是没有找到从起点s到终点t的严格次短路,则输出-1。否则输出起点s到终点t的严格次短路的长度dist2[t],再通过out函数输出次短路的路径。

void solve() {priority_queue<pair<int, int> >q;q.push(make_pair(0, s));memset(dis, ___(3)___, sizeof(dis));memset(pre, -1, sizeof(pre));dis2 = dis + n;pre2 = pre + n;dis[s] = 0;
}

solve函数的Dijkstra堆优化的模板。
优先队列q中保存数对(first, second)的意义是:存在到顶点second的长为first的路径。
首先存在到顶点s的长为0的路径,因此把数对(s, 0)加入优先队列。
优先队列的默认比较规则为less仿函数,该函数传入a、b两个优先队列中的元素,返回a<b,其意义为当满足b>a时,b更优先,优先级最高的元素在优先队列的堆顶。
pair类型已重载<运算符,比较方式是先比较第一个属性first,再比较第二个属性seond。先比较的first属性表示路径长度,first的值越大越优先。而作为求最短路的算法,应该是路径长度越小越优先。为了解决这一点,可以取路径长度的相反数作为pair对象的first属性,first作为负值越大,表明first的相反数越小。这样虽然优先队列是大顶堆,但维护的实际是已知的路径长度最小的路径。

int dis[maxn << 1], *dis2;
int pre[maxn << 1], *pre2;
...
dis2 = dis + n;
pre2 = pre + n;

根据dis数组长度为2*maxn,以及dis2实际指向dis[n],可知dis[0]~dis[n-1]中dis[i]保存的是从s出发到i的最短路径长度。dis[n]~dis[2*n-1]也就是dis2[0]~dis2[n]中dis2[i]表示的是从s出发到i的严格次短路径。
同理,pre[n]~pre[2*n-1]也就是pre2[0]~pre2[n]
pre[i]是顶点从s到i的最短路径上i的前一个顶点。
pre2[i]是顶点从s到i的严格次路径上i的前一个顶点。
主函数中,dis2[t]和inf比较,可知dis2数组,也就是dis数组的初值为inf。
inf的值为522133279,第(3)空每字节的值根据选项可能为0xff,0x1f, 0x3f, 0x7f
经过memset后,int类型dis数组中每个元素的值为4字节,
如每字节的值为0xff,元素的初值为0xffffffff
如每字节的值为0x1f,元素的初值为0x1f1f1f1f
如每字节的值为0x3f,元素的初值为0x3f3f3f3f
如每字节的值为0x7f,元素的初值为0x7f7f7f7f
元素初值的区别在十六进制下的右边数第2位,要想得到这一位的数字需要将元素值先整除16再模16。
⌊ 522133279 16 ⌋ m o d 16 = 1 \lfloor \frac{522133279}{16}\rfloor \mod16=1 16522133279mod16=1,所以每字节的值为0x1f,第(3)空选B。
pre初值都设为-1。

	while (!q.empty()) {int aa = q.top().second; q.pop();if (vis[aa])continue;vis[aa] = true;int a = aa % n;for (int e = head[a]; e ; e = nxt[e]) {int b = to[e], c = w[e];if (aa < n) {if (!upd(a, b, dis[a] + c, q))___(4)___ } else {upd(n + a, n + b, dis2[a] + c, q);}}}

vis[i]的意义是顶点i是否已出队。对于Dijkstra堆优化算法,从起点到已经出队的顶点的最短路径长度不会再更新,因为后入队的路径的长度一定大于到该顶点的路径长度。因此如果堆顶元素中second是已经出队的顶点,就不再更新到其邻接点的最短路径。这就是为什么有if(vis[aa]) continue;
因为aa可能是大于等于n,用来表示次短路的顶点编号,所以先进行a = aa%n把aa处理成其对应的真正顶点编号a。
遍历a的邻接点,b是a的一个邻接点,c是便<a, b>的权值。
如果aa<n,说明从优先队列出队的是一条从起点s到a的最短路径,这样的路径可以更新最短路以及次短路。
否则如果aa>=n,那么从优先队列出队的是一条从起点s到a的次短路径,这样的路径只可能更新次短路径。
更新次短路的核心思路为:
如果新路径比最短路径短更短,那么原最短路变为次短路,新路径作为最短路。
否则,如果新路径比严格次短路短,但是比最短路长,那么新路径作为严格次短路。

bool upd(int a, int b, int d, priority_queue<pair<int, int> > &q) {if (d >= dis[b])return false;if (b < n) ___(1)___;q.push(___(2)___);dis[b] = d;pre[b] = a;return true;
}

upd的意思是update,更新。
结合调用语句upd(a, b, dis[a] + c, q)可知
参数a、b、d的意义是从存在一条从起点s到b的长为d的路径,最后一步是从a走到b的。
参数q是优先队列的引用,一直传q即可。
如果新路径长度d比已有的路径长度dis[b](可能是最短或次短路)更长,则不发生更新,返回false。
如果终点b<n,则当前到b新路径比到b的最短路长度更小,需要使用当前的最短路长度dis[b]更新到b的次短路长度dis2[b],实现方法为再次调用upd函数,参数a填路径上b的前一个顶点pre[b],参数b填b,参数d为dis[b],所以第(1)空填udp(pre[b],n+b,dis[b],q),选A。
无论b<n时dis[b]表示最短路,还是b>=n时dis[b]表示次短路,都需要使用新的到顶点b的长为d的路径来更新自己。
前文提过,在优先队列中保存的pair的first属性应该是路径长度的相反数,second属性为到达顶点。因此第(2)空填make_pair(-d, b),选A。
接下来将到顶点b的距离更新为d,路径上b的前一个顶点设为a。
只要成功更新路径,就返回true。

			if (aa < n) {if (!upd(a, b, dis[a] + c, q))___(4)___ } else {upd(n + a, n + b, dis2[a] + c, q);}

如果upd(a, b, dis[a] + c, q)返回假,也就是到顶点b的路径长度dis[a]+c大于等于最短路dis[b],不能更新最短路,那么尝试更新次短路。
调用upd函数更新到顶点b的次短路,前一个顶点是a,第一个参数传a。到b的次短路长度保存在dis[b+n],因此第二个参数传b+n,路径长度为dis[a]+c。因此第(4)空填upd(a, b+n, dis[a]+c, q),选A。
如果aa>=n,则出队的路径是个次短路径,只能更新次短路径,此时到达的顶点为n+b,其前一个顶点为aa,也就是a+n。新的路径长度为dis[a+n]+c,也就是dis2[a]+c。
调用solve函数,完成Dijkstra堆优化算法,求出了dis数组,其中包含到每个顶点的最短路和次短路的信息,pre数组保存了到每个顶点的最短路和次短路上该顶点的前一个顶点。

void out(int a) {if (a != s) {if (a < n) out(pre[a]);else out(___(5)___);}printf("%d%c", a % n + 1, " \n"[a == n + t]);
}

如果存在从s到t的次短路,最后输出次短路长度和次短路径。
先看输出语句,a % n + 1是无论a<n还是a>=n,a%n都将a处理成其表示的顶点编号,再加1,是因为原题目中顶点编号从1~n,而代码中顶点编号从0~n-1。
" \n"[a == n + t]是一种很诡异的写法," \n"相当于char s[2] = {' ', '\n'}中的s。
a != n+t时," \n"[0],就是s[0],是' '
a == n+t时,也就是传入的a是次短路的终点t时," \n"[1],就是s[1],是'\n'

如果a==s,则是递归出口,只做输出。
如果a!=s,输出从起点s到a的次短路径,就是先输出从起点s到a的前一个顶点pre[a]的次短路径,再输出顶点a。
调用out(pre[a])就可以输出从起点s到a的前一个顶点pre[a]的次短路径,无论a是否小于n,pre[a]都保存了次短路径上a的前一个顶点。
a<n时,调用了out(pre[a])
a>=n时,也应该调用out(pre[a]),但是没有这个选项,看一看有没有等价的选项。
pre[a] = pre[a-n+n] = pre2[a-n] = pre2[a%n]
因此第(5)空填pre2[a%n],选A。

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

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

相关文章

TFTP协议

目录 一、TFTP协议概述 1.1 TFTP协议简介 1.2 TFTP协议特点 二、TFTP协议原理 2.1 TFTP协议工作流程 2.2 TFTP协议数据包格式 三、TFTP协议应用场景 3.1 网络设备配置文件传输 3.2 虚拟机镜像文件传输 3.3 IoT设备固件升级 四、TFTP协议优化方法 4.1 增加超时重传机…

深入理解Python中的数据结构:OrderedDict

目录 1. 前言 2. OrderedDict的基本概念 2.1 OrderedDict的创建 2.2 排序特性 2.3 比较OrderedDict和标准字典 3. OrderedDict的高级功能 3.1 元素的移动 3.2 重新排序 3.3 反转顺序 4. OrderedDict的性能表现 4.1 插入性能测试 4.2 读取性能测试 5. OrderedDict的…

数据结构之——队列

一、队列概述 队列是一种操作受限的线性表&#xff0c;其限制条件为允许在表的一端进行插入&#xff0c;而在表的另一端进行删除。插入的一端叫做队尾&#xff0c;删除的一端叫做队头。向队列中插入新元素的行为称为进队&#xff0c;从队列中删除元素的行为称为出队。例如军训的…

Java服务端开发中的API版本管理:从URI到Header的不同策略

Java服务端开发中的API版本管理&#xff1a;从URI到Header的不同策略 大家好&#xff0c;我是微赚淘客返利系统3.0的小编&#xff0c;是个冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;在Java服务端开发中&#xff0c;API版本管理是一个重要而复杂的问题。随着业…

前端文件上传全过程

特别说明&#xff1a;ui框架使用的是蚂蚁的antd 这里主要是学习前端上传接口的传递参数包括前端上传之前对于代码的整理 一、第一步将前端页面画出来 源代码&#xff1a; /** 费用管理 - IT费用管理 - 费用数据上传 */ import { useState } from "react"; import {…

Leetcode 56.合并区间-Python

链接&#xff1a;56. 合并区间 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff…

【基础知识】网络套接字编程

套接字 IP地址 port&#xff08;端口号&#xff09; socket&#xff08;套接字&#xff09; socket常见API //创建套接字 int socket(int domain, int type, int protocol); //绑定端口 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); //监听套接字…

Prometheus篇之利用promtool工具校验配置正确性

promtool工具 promtool是Prometheus的一个命令行工具&#xff0c;它提供了一些功能来帮助用户进行Prometheus配置文件&#xff08;如prometheus.yml&#xff09;的检查、规则检查和调试。 解释 promtool check config: 验证Prometheus配置文件的语法和设置。 promtool命令&…

【最基础最直观的排序 —— 选择排序算法】

最基础最直观的排序 —— 选择排序算法 选择排序算法是一种简单直观的排序算法。其基本思想是每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;存放在序列的起始位置&#xff0c;然后&#xff0c;再从剩余未排序元素中继续寻找最小&a…

vue3常用的组件间通信

一 props props 可以实现父子组件通信&#xff0c;props数据是只读。 1. 基本用法 在父组件中&#xff0c;你可以这样传递 props&#xff1a; <template><ChildComponent message"Hello, Vue 3!" /> </template><script setup> import C…

WebPage-Bootstrap框架(container类,container-fluid类,栅格系统)

1.Bootstrap Bootstrap为页面内容和栅格系统包裹了一个.container容器&#xff0c;框架预先定义类 1.1container类 响应式布局容器的宽度 手机-小于768px 宽度设置100%&#xff1b; 平板-大于等于768px 设置宽度为750px 桌面显示器-大于等于992px 设置宽度 970px 大屏幕显…

【Bug解决】Nacos启动成功,但却无法访问(提示:无法访问此网站,192.168.10.88的响应时间过长)

情形如下&#xff1a;第一次启动运行一些正常&#xff0c;非正常关闭虚拟机&#xff0c;第二次启动虚拟机查看容器状态如下&#xff1a; docker nacos容器一切正常启动&#xff0c;但是就是无法访问web控制面板&#xff0c;访问无法连接。 首先&#xff1a;执行命令查看日志 …

设置元素浮动,出现高度塌陷导致鼠标无法点击

先贴代码片段&#xff1a; <div style"width: 30%; float: left;"><nz-input-group nzSearch [nzAddOnAfter]" suffixIconButton"><input type"text" nz-input placeholder"请输入实验名称" [(ngModel)]"q.name&…

glTF格式:WebGL应用的3D资产优化解决方案

摘要 glTF作为一种高效的3D资产格式&#xff0c;为WebGL、OpenGL ES和OpenGL运行时的应用提供了强有力的支持。它不仅简化了3D模型的传输与加载流程&#xff0c;还通过优化资产大小&#xff0c;使得打包、解包更加便捷。本文将深入探讨glTF格式的优势&#xff0c;并提供实用的代…

ClickHouse 与 Quickwit 集成实现高效查询

1. 概述 在当今大数据分析领域&#xff0c;ClickHouse 作为一款高性能的列式数据库&#xff0c;以其出色的查询速度和对大规模数据的处理能力&#xff0c;广泛应用于在线分析处理 (OLAP) 场景。ClickHouse 的列式存储和并行计算能力使得它在处理结构化数据查询时极具优势&…

初探shell与bash使用指南

文章目录 一、shell二、bash第一步、新建脚本第二步、添加权限第三步、执行bash脚本 在日常开发中&#xff0c;经常使用到Linux服务器相关知识&#xff0c;输入命令获取想要的结果&#xff0c;本篇介绍shell 与 bash的相关知识。 一、shell 是命令行解释器&#xff0c;接收用户…

nlohmann json:读写json文件

读写json文件是经常的操作,可以通过如下的方式完成: #include <string> #include <iostream> #include <fstream> #include <filesystem> #include <nlohmann/json.hpp>using namespace std; using json = nlohmann::json; namespace fs = st…

使用nc命令检测UDP端口

使用nc命令检测UDP端口也是非常的简单&#xff0c;需要注意的是&#xff0c;所安装nc的版本不同&#xff0c;使用选项有点差异。 1、检测开启的UDP nc -vuz 192.168.2.201 5353 nc -vuz 192.168.2.201 37430 端口正常启用时&#xff0c;会提示“UDP packet sent successful…

深入解析网络通信关键要素:IP 协议、DNS 及相关技术

我的主页&#xff1a;2的n次方_ 1. IP 协议报头结构 4 位版本&#xff1a;表示 IPv4 / IPv6 4 位首部长度&#xff1a;表示 IP 报头的长度&#xff0c;以 4 字节为单位 8 位服务类型&#xff1a;包括 3 位优先权字段&#xff08;已弃用&#xff09;&#xff0c;4 位 TOS 字…

电路 - 笔记2

1 555 芯片 2 类比 - pU*I 与 Fm*a 是不是可以与牛顿定律类比 - Fm*a 人的力量&#xff08;F&#xff09;有限。 当推大箱子&#xff08;m&#xff09;时&#xff0c;加速度&#xff08;a&#xff09;就不会很大 当推小箱子&#xff08;m&#xff09;时&#xff0c;加速度…