Редакция для Короткая дорога курьера
Submitting an official solution before solving the problem yourself is a bannable offence.
1. Идея
Нужно найти минимальное число дорог, которое надо проехать от перекрестка s до перекрестка t в неориентированном графе.
Так как каждая дорога имеет одинаковую "стоимость" — один участок пути, задача сводится к поиску кратчайшего пути по числу ребер в невзвешенном графе.
Для таких задач стандартный и самый подходящий метод — BFS, то есть обход в ширину.
2. Наблюдения
- Перекрестки — это вершины графа, дороги — ребра.
- Все дороги равноправны: переход по любой дороге увеличивает длину пути ровно на
1. - BFS как раз умеет находить минимальное количество ребер от стартовой вершины до всех остальных.
- Если в процессе BFS вершина
tокажется достижимой, то найденное расстояние будет минимальным. - Если после обхода
dist[t]осталось равным-1, значит пути не существует.
Также важно заметить частный случай:
- если
s = t, то ответ сразу0, потому что курьер уже находится в нужной точке.
В предложенном решении этот случай отдельно обрабатывать не нужно: BFS сам корректно даст dist[s] = 0.
3. Алгоритм
- Считываем
n,m,s,t. - Строим список смежности:
- для каждой дороги
u vдобавляемvв список соседейu, - и
uв список соседейv, так как граф неориентированный.
- для каждой дороги
- Создаем массив
distдлиныn + 1и заполняем его значениями-1.dist[v]будет хранить минимальное число дорог отsдо вершиныv.
- Запускаем BFS из вершины
s:- кладем
sв очередь, - ставим
dist[s] = 0.
- кладем
- Пока очередь не пуста:
- берем вершину
vиз начала очереди, - перебираем всех соседей
to, - если
toеще не посещена, то:- присваиваем
dist[to] = dist[v] + 1, - добавляем
toв очередь.
- присваиваем
- берем вершину
- После завершения BFS выводим
dist[t].
4. Почему это работает
Обход в ширину посещает вершины слоями:
- сначала все вершины на расстоянии
0отs, то есть саму вершинуs; - потом все вершины на расстоянии
1; - потом все вершины на расстоянии
2; - и так далее.
Когда BFS впервые приходит в некоторую вершину to, он делает это по кратчайшему пути по числу ребер. Почему так?
Потому что:
- все вершины с меньшим расстоянием уже были обработаны раньше;
- ребра имеют одинаковую цену
1; - значит, первое найденное расстояние до вершины уже минимально.
Следовательно:
dist[v]после BFS действительно равно минимальному количеству дорог отsдоv;- в частности,
dist[t]— это искомый ответ.
Если же t так и не была посещена, то до нее нельзя добраться из s, поэтому нужно вывести -1.
5. Сложность
Обозначим:
n— число перекрестков,m— число дорог.
Тогда:
- построение графа занимает
O(m); - BFS обходит каждую вершину не более одного раза и каждое ребро просматривает не более двух раз, поэтому работает за
O(n + m).
Итоговая сложность:
- по времени:
O(n + m); - по памяти:
O(n + m).
Это подходит для ограничений до 100000 вершин и 200000 ребер.
6. Код на C++17
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int main() {
int n, m, s, t;
cin >> n >> m >> s >> t;
vector<vector<int>> g(n + 1);
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
vector<int> dist(n + 1, -1);
queue<int> q;
dist[s] = 0;
q.push(s);
while (!q.empty()) {
int v = q.front();
q.pop();
for (int to : g[v]) {
if (dist[to] == -1) {
dist[to] = dist[v] + 1;
q.push(to);
}
}
}
cout << dist[t] << '\n';
return 0;
}
7. Код на Python 3
from collections import deque
n, m, s, t = map(int, input().split())
g = [[] for _ in range(n + 1)]
for _ in range(m):
u, v = map(int, input().split())
g[u].append(v)
g[v].append(u)
dist = [-1] * (n + 1)
q = deque([s])
dist[s] = 0
while q:
v = q.popleft()
for to in g[v]:
if dist[to] == -1:
dist[to] = dist[v] + 1
q.append(to)
print(dist[t])
Комментарии