К содержанию

Подтема 4 из 6

Условия на путь

Путь через вершину, в обход вершины, через ребро, с заданным первым и последним ребром — та же функция f.

Цель — параметр

В условии спрашивают путь не до 100, а до другой вершины — или через промежуточную. Как не переписывать функцию каждый раз?

Сделать цель вторым параметром: f(v, t) — длина кратчайшего пути из v в t. В функции меняется одна строка — сравниваем v не с 100, а с t. @cache запоминает ответ для каждой пары (v, t), так что разные цели не смешиваются.

from functools import cache graph = {}for line in open("23.txt"):    a, b, w = line.split()    a, b, w = int(a), int(b), float(w)    if a not in graph:        graph[a] = []    graph[a].append((b, w))  @cachedef f(v, t):    if v == t:        return 0    best = float("inf")    for u, w in graph.get(v, []):        best = min(best, w + f(u, t))    return best  print(f(1, 100), f(1, 4), f(6, 12))
7.5 2.5 9.5

Из 6 в 12 лучший путь — 6 → 1 → 7 → 100 → 12: 1.0 + 5.5 + 2.0 + 1.0 = 9.5. Дальше в подтеме все условия собираем из таких кусков.

Через вершину

Путь из 1 в 100 должен пройти через вершину 4. Как найти самый короткий такой путь?

Разрезать его в вершине 4 на два участка и сложить их минимумы: f(1, 4) + f(4, 100). Любой путь через 4 — это участок до 4 и участок после. Каждый не короче своего минимума, а два лучших участка всегда можно склеить в 4.

print(f(1, 4) + f(4, 100))
10.5

Получилось 2.5 + 8.0 = 10.5 — дороже, чем 7.5 без условия: лучший путь через 4 не проходит.

Обязательная вершина 4 обведена. Кратчайший путь через неё — 1 → 4 → 100, его длина 10.5.

Если обязательных вершин две, участков три. В каком порядке их проходить, угадывать не нужно: посчитай оба порядка и возьми меньший. Например, путь из 6 в 100 через 1 и 7:

a = f(6, 1) + f(1, 7) + f(7, 100)b = f(6, 7) + f(7, 1) + f(1, 100)print(a, b, min(a, b))
8.5 inf 8.5

Из 7 в 1 пути нет, поэтому второй порядок дал бесконечность. В графе без циклов иначе и не бывает: если бы из 1 можно было попасть в 7, а из 7 — в 1, получился бы цикл. Подходит не больше одного порядка.

В обход вершины

А если через вершину 7 проходить нельзя?

Сделать её тупиком: в запрещённой вершине функция сразу возвращает бесконечность. Тогда ни одна стрелка в 7 не выиграет минимум — так же, как стрелка в настоящий тупик в подтеме 2.

BAN = 7  @cachedef g(v, t):    if v == BAN:        return float("inf")    if v == t:        return 0    best = float("inf")    for u, w in graph.get(v, []):        best = min(best, w + g(u, t))    return best  print(g(1, 100), g(6, 100))
10.5 11.5

Проверка запрета стоит первой: даже если запрещена сама цель, войти в неё нельзя. Для запрета пишут отдельную функцию со своим @cache: ответы f без запрета к ней не подходят.

Вершина 7 запрещена, её стрелки не работают. Лучший оставшийся путь из 1 — 1 → 4 → 100 за 10.5.

С запретом пути может и не остаться — тогда функция вернёт inf, а int(inf) остановит программу ошибкой. В задачах экзамена пишут «существование пути гарантируется»; если такой фразы нет, сначала выведи само значение.

Через ребро

Путь из 6 в 100 должен пройти по ребру 6 → 7. Как это учесть?

Путь через ребро a → b — это участок до a, само ребро и участок от b: f(s, a) + вес ребра + f(b, t). Вес ребра берут из списка вершины a. dict(graph[6]) превращает список пар в словарь «куда → вес»:

print(dict(graph[6]))print(f(6, 6) + dict(graph[6])[7] + f(7, 100))
{7: 7.0, 1: 1.0} 9.0

Участок до 6 пустой — путь и так начинается в 6, поэтому f(6, 6) = 0. Вышло 7.0 + 2.0 = 9, хотя без условия путь из 6 стоит 8.5.

Обязательное ребро 6 → 7. Кратчайший путь по нему — 6 → 7 → 100 за 9.0; путь 6 → 1 → 7 → 100 короче, но идёт мимо этого ребра.

Так же решают задачу, где заданы первое и последнее ребро пути. Из 6 в 12, первое ребро 6 → 7, последнее 100 → 12: вес первого, плюс f(7, 100), плюс вес последнего — 7.0 + 2.0 + 1.0 = 10.

Итого

  • Цель — параметрf(v, t) — путь из v в t
  • Через вершину kf(s, k) + f(k, t)
  • Через две вершиныоба порядка, взять меньший
  • В обход вершиныотдельная функция: в запрещённой вершине ∞
  • Через ребро a → bf(s, a) + вес + f(b, t)
  • Вес ребраdict(graph[a])[b]
  • Пути нетфункция вернёт inf, от неё — ошибка

Проверьте себя

Ответь сначала сам, потом открой ответ.

Вопрос 1
Найди длину кратчайшего пути из 1 в 100 через вершину 7 в учебном графе.
ОтветСкрыть ответ
Ответ
f(1, 7) + f(7, 100) = 5.5 + 2.0 = 7.5 — как и без условия: кратчайший путь и так идёт через 7.
Вопрос 2
А через вершину 6?
ОтветСкрыть ответ
Ответ
Такого пути нет: в 6 не входит ни одна стрелка, f(1, 6) = ∞.
Вопрос 3
Найди кратчайший путь из 6 в 100 в обход вершины 7. Почему он не 8.5?
ОтветСкрыть ответ
Ответ
6 → 1 → 4 → 100 за 11.5. Путь за 8.5 — это 6 → 1 → 7 → 100, а он проходит через запрещённую 7.
Вопрос 4
Обязательные вершины A и B. Почему в графе без циклов путь может пройти их только в одном порядке?
ОтветСкрыть ответ
Ответ
Если бы из A можно было попасть в B, а из B — в A, получился бы цикл A → … → B → … → A.
Вопрос 5
Найди кратчайший путь из 6 в 12, проходящий по ребру 1 → 100.
ОтветСкрыть ответ
Ответ
f(6, 1) + 12.0 + f(100, 12) = 1.0 + 12.0 + 1.0 = 14: путь 6 → 1 → 100 → 12.

Практика

Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.

Обязательная и запрещённая вершина

Граф задан рёбрами:

1 4 2.51 6 4.14 6 1.24 15 0.66 15 2.96 100 7.415 100 5.815 22 0.3

Найди длину кратчайшего пути из 1 в 100, который проходит через вершину 15 и не проходит через вершину 4.

Введите длину — десятичную дробь через точку.

Путь через обязательную вершину

В текстовом файле 23-2.txt содержится описание ациклического ориентированного взвешенного графа: в каждой строке — номер начальной вершины ребра, номер конечной вершины и вес ребра; числа разделены пробелами и/или табуляциями. Найдите длину кратчайшего пути из вершины 1 в вершину 100, проходящего через вершину 264. В ответ запишите целую часть найденной длины.

23-2.txtАциклический ориентированный граф: в строке — начало ребра, конец и весТекстовый файл · 3,2 КБ

Введите целую часть найденной длины.

Путь через две обязательные вершины

В текстовом файле 23-5.txt содержится описание ациклического ориентированного взвешенного графа: в каждой строке — номер начальной вершины ребра, номер конечной вершины и вес ребра; числа разделены пробелами и/или табуляциями. Найдите длину кратчайшего пути из вершины 1 в вершину 100, проходящего через вершины 342 и 423. В ответ запишите целую часть найденной длины.

23-5.txtАциклический ориентированный граф: в строке — начало ребра, конец и весТекстовый файл · 3,2 КБ

Введите целую часть найденной длины.

Путь в обход запрещённой вершины

В текстовом файле 23-2.txt содержится описание ациклического ориентированного взвешенного графа: в каждой строке — номер начальной вершины ребра, номер конечной вершины и вес ребра; числа разделены пробелами и/или табуляциями. Найдите длину кратчайшего пути из вершины 1 в вершину 100, не проходящего через вершину 975. В ответ запишите целую часть найденной длины.

23-2.txtАциклический ориентированный граф: в строке — начало ребра, конец и весТекстовый файл · 3,2 КБ

Введите целую часть найденной длины.

Путь через ребро

В текстовом файле 23-2.txt содержится описание ациклического ориентированного взвешенного графа: в каждой строке — номер начальной вершины ребра, номер конечной вершины и вес ребра; числа разделены пробелами и/или табуляциями. Найдите длину кратчайшего пути из вершины 1 в вершину 100, проходящего по ребру из вершины 921 в вершину 305. В ответ запишите целую часть найденной длины.

23-2.txtАциклический ориентированный граф: в строке — начало ребра, конец и весТекстовый файл · 3,2 КБ

Введите целую часть найденной длины.

Первое и последнее ребро

В текстовом файле 23-6.txt содержится описание ациклического ориентированного взвешенного графа: в каждой строке — номер начальной вершины ребра, номер конечной вершины и вес ребра; числа разделены пробелами и/или табуляциями. Найдите длину кратчайшего пути из вершины 1 в вершину 100, если первое ребро пути ведёт из 1 в 554, а последнее — из 53 в 100. В ответ запишите целую часть найденной длины.

23-6.txtАциклический ориентированный граф: в строке — начало ребра, конец и весТекстовый файл · 3,0 КБ

Введите целую часть найденной длины.