Подтема 5 из 6
Другие вопросы о путях
Сколько путей, самый длинный путь, наименьшее и наибольшее число рёбер: что поменять в функции f.
Одна схема
В подборках спрашивают не только кратчайший путь: сколько всего путей, какой путь самый длинный, сколько в пути рёбер. Нужна ли для каждого вопроса своя идея?
Нет, функция та же. Меняются три места: что вернуть в цели, что вернуть в тупике и как собрать ответы соседей. Остальное — словарь из файла, перебор стрелок, @cache — не трогаем.
| Вопрос | В цели | В тупике | Как собрать |
|---|---|---|---|
| кратчайший путь | 0 | ∞ | min(w + f(u)) |
| самый длинный путь | 0 | −∞ | max(w + f(u)) |
| число путей | 1 | 0 | сумма f(u) |
| наименьшее число рёбер | 0 | ∞ | min(1 + f(u)) |
| наибольшее число рёбер | 0 | −∞ | max(1 + f(u)) |
Значение в тупике — всегда «худшее, что может быть» для этого вопроса: оно не должно выиграть сравнение. Для минимума это ∞, для максимума −∞, для суммы — 0: путь в тупик в число путей до цели не входит. Разберём каждый вопрос на учебном графе.
Число путей
Сколько путей ведёт из 1 в 100?
Каждый путь из v начинается какой-то стрелкой, поэтому число путей из v — сумма чисел путей из концов её стрелок. В цели путь один — пустой, «уже пришли». Ноль в цели всё бы обнулил: любая сумма нулей — ноль.
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 count(v): if v == 100: return 1 total = 0 for u, w in graph.get(v, []): total += count(u) return total print(count(1), count(6))Из 7 и из 4 к цели ведёт по одному пути, из 1 — 1 + 1 + 1 = 3: через 7, через 4 и напрямую. Из 6 — путь через 7 и три пути через 1, всего 4.
Путей бывают миллиарды, но Python считает целые числа без переполнения, а @cache не даёт перебирать их по одному. Число путей через ребро a → b — произведение: число путей до a, умноженное на число путей от b. Каждый путь до a можно склеить с каждым путём от b.
Самый длинный путь
Какой путь из 1 в 100 самый длинный?
Тот же расчёт с max вместо min. А в тупике теперь −∞ — иначе путь, упёршийся в тупик, мог бы победить.
@cachedef longest(v): if v == 100: return 0 best = float("-inf") for u, w in graph.get(v, []): best = max(best, w + longest(u)) return best print(longest(1), longest(6))Из 1 самый длинный путь — прямая стрелка 1 → 100 за 12.0, из 6 — 6 → 1 → 100 за 13.0. Если бы из 1 вела стрелка в тупик весом 20, то с нулём в тупике ответ стал бы 20 — длина пути, который до 100 не доходит. С −∞ такая стрелка никогда не выигрывает максимум.
Бывает и другой вопрос: самый длинный путь из вершины до любого тупика. Тогда цели нет, и ноль возвращают как раз в тупике — там, где путь закончился:
@cachedef to_dead_end(v): if v not in graph: return 0 best = float("-inf") for u, w in graph[v]: best = max(best, w + to_dead_end(u)) return best print(to_dead_end(6))Из 6 это 6 → 1 → 100 → 12: 1.0 + 12.0 + 1.0 = 14. Тупиков в этом графе один — вершина 12.
Число рёбер
Из какого наименьшего числа рёбер может состоять путь из 1 в 100?
Это тот же кратчайший путь, только каждое ребро весит 1: вместо w прибавляем 1. Веса из файла читаем, как обычно, но не используем.
@cachedef fewest(v): if v == 100: return 0 best = float("inf") for u, w in graph.get(v, []): best = min(best, 1 + fewest(u)) return best print(fewest(1), fewest(6))Из 1 хватает одного ребра — 1 → 100, из 6 нужно два: 6 → 7 → 100. Наибольшее число рёбер — тот же код с max и −∞ в тупике: из 1 это 2, из 6 — 3.
Итого
- Что меняетсязначение в цели, в тупике и как собрать ответы
- Число путейв цели 1, в тупике 0, сумма
- Через ребро a → bпутей до a × путей от b
- Самый длинный путьmax, в тупике −∞
- До любого тупикацели нет, 0 в тупике, max
- Число рёбервместо веса прибавляем 1
Проверьте себя
Ответь сначала сам, потом открой ответ.
ОтветСкрыть ответ
ОтветСкрыть ответ
ОтветСкрыть ответ
ОтветСкрыть ответ
ОтветСкрыть ответ
Практика
Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.
Пути через ребро
В текстовом файле 23-4.txt содержится описание ациклического ориентированного взвешенного графа: в каждой строке — номер начальной вершины ребра, номер конечной вершины и вес ребра; числа разделены пробелами и/или табуляциями. Определите количество различных путей из вершины 1 в вершину 100, проходящих по ребру из вершины 472 в вершину 139.
Введите число.
Подсказка
Число путей до 472, умноженное на число путей от 139.
Решение
81: каждый путь до 472 склеивается с каждым путём от 139.
Решение программой
from functools import cache graph = {}for line in open("23-4.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 count(v, t): if v == t: return 1 total = 0 for u, w in graph.get(v, []): total += count(u, t) return total print(count(1, 472) * count(139, 100))Ответ: 81
Самый длинный путь
В текстовом файле 23-4.txt содержится описание ациклического ориентированного взвешенного графа: в каждой строке — номер начальной вершины ребра, номер конечной вершины и вес ребра; числа разделены пробелами и/или табуляциями. Найдите длину самого длинного пути из вершины 1 в вершину 100. В ответ запишите целую часть найденной длины.
Введите целую часть найденной длины.
Подсказка
Вместо минимума — максимум, а в тупике минус бесконечность.
Решение
7362.
Решение программой
from functools import cache graph = {}for line in open("23-4.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 longest(v): if v == 100: return 0 best = float("-inf") for u, w in graph.get(v, []): best = max(best, w + longest(u)) return best print(int(longest(1)))Ответ: 7362
Самый длинный путь до тупика
В текстовом файле 23-6.txt содержится описание ациклического ориентированного взвешенного графа: в каждой строке — номер начальной вершины ребра, номер конечной вершины и вес ребра; числа разделены пробелами и/или табуляциями. Вершины, из которых не выходит ни одного ребра, назовём тупиками. Найдите длину самого длинного пути из вершины 1 до какого-либо тупика. В ответ запишите целую часть найденной длины.
Введите целую часть найденной длины.
Подсказка
Цели нет: в тупике ноль, в остальных — максимум по соседям.
Решение
19996: длина такого пути 19996.04.
Решение программой
from functools import cache graph = {}for line in open("23-6.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 to_dead_end(v): if v not in graph: return 0 best = float("-inf") for u, w in graph[v]: best = max(best, w + to_dead_end(u)) return best print(int(to_dead_end(1)))Ответ: 19996
Наименьшее число рёбер
В текстовом файле 23-3.txt содержится описание ациклического ориентированного взвешенного графа: в каждой строке — номер начальной вершины ребра, номер конечной вершины и вес ребра; числа разделены пробелами и/или табуляциями. Найдите наименьшее количество рёбер, которое может содержать путь из вершины 1 в вершину 100.
Введите число.
Подсказка
Кратчайший путь, где каждое ребро весит 1.
Решение
6 рёбер.
Решение программой
from functools import cache graph = {}for line in open("23-3.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 fewest(v): if v == 100: return 0 best = float("inf") for u, w in graph.get(v, []): best = min(best, 1 + fewest(u)) return best print(fewest(1))Ответ: 6
Наибольшее число рёбер
В текстовом файле 23-3.txt содержится описание ациклического ориентированного взвешенного графа: в каждой строке — номер начальной вершины ребра, номер конечной вершины и вес ребра; числа разделены пробелами и/или табуляциями. Найдите наибольшее количество рёбер, которое может содержать путь из вершины 1 в вершину 100.
Введите число.
Подсказка
max вместо min, в тупике минус бесконечность, каждое ребро весит 1.
Решение
26 рёбер.
Решение программой
from functools import cache graph = {}for line in open("23-3.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 most(v): if v == 100: return 0 best = float("-inf") for u, w in graph.get(v, []): best = max(best, 1 + most(u)) return best print(most(1))Ответ: 26