#P2374. 第2题-小明回家去
-
2000ms
Tried: 1009
Accepted: 277
Difficulty: 6
所属公司 :
华为
时间 :2023年6月14日-暑期实习
-
算法标签>BFS
第2题-小明回家去
题面描述
在这道题中,我们需要帮助塔子规划从起点星球到终点星球的最短路径,给定的路径由若干通行证定义。输入包括通行证的数量和起点、终点的编号,以及每个通行证的起止星球。输出应为塔子需要经过的星球数量(不包括起点),若无法到达终点,则返回 -1。我们可以使用广度优先搜索(BFS)算法来解决此问题,通过构建星球间的图结构,逐层探索可达星球,以找到最短路径或判断不可达。
思路: bfs
朴素的bfs求最短路的题
算法步骤:
- 图的表示:使用邻接表来表示图。对于每个通行证(边),将起点和终点加入到相应的列表中。
- 初始化:创建一个数组
vis用于标记每个星球是否被访问过,以及记录到达该星球所需的步数。 - BFS搜索:
- 初始化队列,将起点放入队列。
- 进行循环,直到队列为空。在每一层中,遍历当前层的所有星球,并对每个星球的相邻星球进行处理。
- 如果相邻星球尚未被访问,则更新其访问步数,并将其加入队列。
- 步数在每一层结束时自增,确保每次都能正确计算到达每个星球的最小步数。
- 结果输出:检查终点星球的访问步数,如果仍为 0,则表示不可达,输出 -1;否则输出访问步数。
代码
C++
#include<bits/stdc++.h>
using namespace std;
// 定义邻接表
vector<vector<int>> edge;
int vis[100010]; // 访问数组,用于记录每个星球的访问状态和步数
int m1, m2; // 起点和终点星球编号
int main()
{
edge.resize(100010); // 初始化邻接表
int n;
cin >> n >> m1 >> m2; // 输入通行证数量和起终点编号
// 读取通行证信息,构建图
for (int i = 0; i < n; i++)
{
int x, y;
cin >> x >> y; // 输入每个通行证的起点和终点
edge[x].push_back(y); // 建立有向边
}
int step = 0; // 步数计数器
queue<int> qu; // 队列用于 BFS
qu.push(m1); // 将起点加入队列
while (!qu.empty()) // 当队列不为空时继续进行 BFS
{
int sz = qu.size(); // 当前层的节点数量
for (int i = 0; i < sz; i++)
{
int top = qu.front(); // 获取队列头部元素
qu.pop(); // 弹出队列头部元素
for (int v : edge[top]) // 遍历当前节点的所有邻接节点
{
if (vis[v]) // 如果邻接节点已经被访问过
continue; // 跳过该节点
vis[v] = step + 1; // 更新访问步数
qu.push(v); // 将邻接节点加入队列
}
}
// 步数自增,准备处理下一层节点
step++;
}
// 判断终点是否被访问过
if (vis[m2] == 0)
cout << -1 << endl; // 如果没有访问到终点,输出 -1
else
cout << vis[m2] << endl; // 否则输出到达终点的步数
return 0; // 程序结束
}
Python
import sys
from collections import deque, defaultdict
# 广度优先搜索函数,返回从起点到终点的最短路径长度
def bfs(graph, start, end):
# 如果起点和终点相同,直接返回 0
if start == end:
return 0
# 初始化队列,存储当前节点和到达该节点的距离
queue = deque([(start, 0)])
# 使用集合来记录已访问的节点
visited = set([start])
# 开始 BFS 循环
while queue:
current, distance = queue.popleft() # 弹出队列的第一个元素
# 遍历当前节点的所有邻居
for neighbor in graph[current]:
if neighbor not in visited: # 如果邻居节点尚未被访问
if neighbor == end: # 如果找到终点
return distance + 1 # 返回当前距离加一
visited.add(neighbor) # 标记邻居为已访问
queue.append((neighbor, distance + 1)) # 将邻居加入队列,距离加一
return -1 # 如果队列遍历完仍未找到终点,返回 -1
# 主函数
def main():
input = sys.stdin.read # 从标准输入读取所有数据
data = input().split() # 将输入数据按空格分割为列表
index = 0
# 读取通行证数量、起点和终点
n = int(data[index]) # 通行证数量
index += 1
m1 = int(data[index]) # 起点编号
index += 1
m2 = int(data[index]) # 终点编号
index += 1
# 初始化图的邻接表
graph = defaultdict(list)
for _ in range(n):
u = int(data[index]) # 读取边的起点
index += 1
v = int(data[index]) # 读取边的终点
index += 1
graph[u].append(v) # 将边添加到图中
# 调用 BFS 函数计算从 m1 到 m2 的最短路径
result = bfs(graph, m1, m2)
print(result) # 输出结果
# 程序入口
if __name__ == "__main__":
main() # 执行主函数
Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in); // 创建输入扫描器
// 读取输入的通行证数量、起点和终点
int n = sc.nextInt(); // 通行证数量
int start = sc.nextInt(); // 起点编号
int end = sc.nextInt(); // 终点编号
// 使用哈希表来存储图的邻接表,键是起点,值是与之相连的终点集合
Map<Integer, Set<Integer>> map = new HashMap<>();
// 读取每个通行证的信息,构建图
for (int i = 0; i < n; i++) {
int from = sc.nextInt(); // 通行证的起点
int to = sc.nextInt(); // 通行证的终点
// 获取起点对应的终点集合,如果不存在则创建一个新的集合
Set<Integer> set = map.getOrDefault(from, new HashSet<>());
set.add(to); // 将终点添加到集合中
map.put(from, set); // 更新哈希表
}
// 如果起点在图中不存在,直接输出 -1
if(!map.containsKey(start)){
System.out.println(-1);
return;
}
// 初始化队列用于广度优先搜索
Queue<Integer> queue = new LinkedList<>();
queue.add(start); // 将起点加入队列
int cnt = 0; // 步数计数器
Set<Integer> set = new HashSet<>(); // 记录已访问的节点
set.add(start); // 将起点标记为已访问
// 开始广度优先搜索
while (!queue.isEmpty()){
int size = queue.size(); // 当前层节点数量
for (int i = 0; i < size; i++) {
int from = queue.poll(); // 从队列中取出一个节点
// 检查是否到达终点
if(from == end){
System.out.println(cnt); // 输出当前步数
return;
}
// 如果当前节点在图中有邻接节点
if(map.containsKey(from)){
for(int to : map.get(from)){ // 遍历所有邻接节点
// 如果邻接节点尚未被访问
if(!set.contains(to)){
set.add(to); // 标记为已访问
queue.add(to); // 将其加入队列
}
}
}
}
cnt++; // 步数自增,进入下一层搜索
}
System.out.println(-1); // 如果无法到达终点,输出 -1
}
}
javaScript
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
async function readInput() {
let lines = [];
for await (const line of rl) {
lines.push(line);
}
return lines;
}
(async function main() {
let lines = await readInput();
let [n, start, end] = lines[0].split(" ").map(Number);
// 边界处理
if (start === end) {
console.log(0);
return;
}
if (n === 0) {
console.log(-1);
return;
}
// 构建邻接表
let graph = Array.from({ length: 100001 }, () => []);
for (let i = 1; i <= n; i++) {
let [from, to] = lines[i].split(" ").map(Number);
graph[from].push(to);
}
// BFS 进行最短路径搜索
let queue = [start]; // BFS 队列
let visited = new Set();
visited.add(start);
let steps = 0;
while (queue.length > 0) {
let size = queue.length;
for (let i = 0; i < size; i++) {
let from = queue.shift();
// 如果到达终点
if (from === end) {
console.log(steps);
return;
}
for (let to of graph[from]) {
if (!visited.has(to)) {
visited.add(to);
queue.push(to);
}
}
}
steps++;
}
console.log(-1); // 无法到达
})();
题目内容
星际漫游是需要通行证的,塔子收集多年,希望来一次跨越群星的路程,去到一个叫做蓝星的星球,听说那里有一个二字游戏,非常好玩。
很不幸的是,塔子只有若干星球之间的通行证,为了尽快到达心心念念的蓝星,塔子决定找你帮忙规划一下线路。这里规定星际漫游的耗时与经历的星球数成正比。
规定,所有的星球都被编号,从1开始。
解答要求
时间限制:C/C++800ms,其他语言:1600ms 内存限制:C/C++60MB,其他语言: 120MB
输入
塔子拥有的通行证数目n以及塔子漫游的起点编号m1(0≤m1≤100000)和终点编号m2(0≤m2≤100000),
之后n行依次为n个通行证,每个通行证有两个数字,用空格隔开,分别表示该通行证允许通行的起点星球编号和终点星球编号。
注意,只能从起点星球飞往终点星球。
输出
计算塔子需要经过的星球数目,不包括起点星球,若无法到达目的地,则返回−1
样例
输入
1 1 2
1 2
输出
1
解释
本例表示:塔子有1个通行证,刚好直达蓝星,经过蓝星,故输出1。
样例2
输入
3 2 1
1 4
3 2
3 5
输出
-1
解释
拥有的三个通行证无法保证塔子能从编号为2的星球到达编号为1的蓝星,塔子因玩不到二字游戏悲痛欲绝,因此结果为−1