Предложите алгоритм поиска кратчайшего пути в графе с отрицательными ребрами, где циклы отрицательной длины исключены, сопроводите обсуждение сложностей и применимости (Bellman–Ford vs Dijkstra with modifications)
Кратко — алгоритмы и когда применять. 1) Bellman–Ford (один источник, допускает отриц. ребра, требование: нет отриц. циклов) - Идея: инициализировать d(s)=0d(s)=0d(s)=0, остальные d(v)=+∞d(v)=+\inftyd(v)=+∞; повторно «релаксировать» все ребра ровно ∣V∣−1|V|-1∣V∣−1 раз. После этого расстояния корректны; дополнительная итерация детектирует отриц. цикл, если какие‑то ddd изменились. - Псевдокод (сжато): для i=1i=1i=1 до ∣V∣−1|V|-1∣V∣−1: для каждого ребра (u,v)(u,v)(u,v) если d(u)+w(u,v)<d(v)d(u)+w(u,v)<d(v)d(u)+w(u,v)<d(v) то d(v)=d(u)+w(u,v)d(v)=d(u)+w(u,v)d(v)=d(u)+w(u,v). После: ещё одна итерация — если есть улучшение, то есть отриц. цикл. - Сложность: O(∣V∣⋅∣E∣)\mathcal{O}(|V|\cdot|E|)O(∣V∣⋅∣E∣) по времени, O(∣V∣+∣E∣)\mathcal{O}(|V|+|E|)O(∣V∣+∣E∣) по памяти. - Применимость: надежен для single‑source в графах с отриц. ребрами; обнаруживает отриц. циклы. 2) Dijkstra напрямую — нельзя, если есть отриц. ребра (жадность нарушается). - Пример провала: наличие отрицательного ребра может сделать уже «закрытое» расстояние улучшенным через более длинный путь. 3) Johnson (переход к неотрицательным весам) — когда нужно all‑pairs или хочется использовать Dijkstra после пересчёта - Шаги: 1. Добавить вспомогательную вершину s′s's′ со сведениями w(s′,v)=0w(s',v)=0w(s′,v)=0 для всех vvv. 2. Запустить Bellman–Ford от s′s's′ и получить потенциалы h(v)=δ(s′,v)h(v)=\delta(s',v)h(v)=δ(s′,v). Если найден отриц. цикл — останов. 3. Переопределить веса: w′(u,v)=w(u,v)+h(u)−h(v)w'(u,v)=w(u,v)+h(u)-h(v)w′(u,v)=w(u,v)+h(u)−h(v). Тогда все w′≥0w'\ge 0w′≥0. 4. Для каждого источника sss запустить Dijkstra с весами w′w'w′, получив d′(v)d'(v)d′(v). Восстановить оригинальные расстояния: d(v)=d′(v)−h(s)+h(v)d(v)=d'(v)-h(s)+h(v)d(v)=d′(v)−h(s)+h(v). - Сложность: Bellman–Ford O(∣V∣⋅∣E∣)\mathcal{O}(|V|\cdot|E|)O(∣V∣⋅∣E∣) + для каждого источника Dijkstra (с кучей) O(∣E∣+∣V∣log∣V∣)\mathcal{O}(|E|+|V|\log|V|)O(∣E∣+∣V∣log∣V∣). Для all‑pairs итог: O(∣V∣⋅∣E∣+∣V∣2log∣V∣)\mathcal{O}(|V|\cdot|E| + |V|^2\log|V|)O(∣V∣⋅∣E∣+∣V∣2log∣V∣) (на практике: O(∣V∣⋅∣E∣+∣V∣⋅(∣E∣+∣V∣log∣V∣))\mathcal{O}(|V|\cdot|E| + |V|\cdot(|E|+|V|\log|V|))O(∣V∣⋅∣E∣+∣V∣⋅(∣E∣+∣V∣log∣V∣))). - Применимость: эффективно для all‑pairs в разреженных графах; для single‑source обычно проще Bellman–Ford (если нет других причин). 4) SPFA (очередь релаксаций) — практический эвристический вариант Bellman–Ford - Часто быстрее на реальных данных, но в худшем случае O(∣V∣⋅∣E∣)\mathcal{O}(|V|\cdot|E|)O(∣V∣⋅∣E∣) (и в некоторых конструкциях — крайне плохой) — использовать осторожно. Рекомендации выбора - Single‑source + отриц. рёбра (и нужно детектировать отриц. цикл) — Bellman–Ford. - All‑pairs + отриц. рёбра (нет отриц. циклов) — Johnson (BF для потенциалов + Dijkstra для каждого источника). - Нет отриц. рёбер — Dijkstra. - Практическая альтернатива для Single‑source: SPFA (быстро обычно, но не гарантированно). Короткое обоснование корректности Bellman–Ford: кратчайший простой путь содержит не более ∣V∣−1|V|-1∣V∣−1 рёбер, поэтому после ∣V∣−1|V|-1∣V∣−1 полных проходов по всем рёбрам все такие пути будут учтены; дополнительная итерация показывает наличие отриц. цикла.
1) Bellman–Ford (один источник, допускает отриц. ребра, требование: нет отриц. циклов)
- Идея: инициализировать d(s)=0d(s)=0d(s)=0, остальные d(v)=+∞d(v)=+\inftyd(v)=+∞; повторно «релаксировать» все ребра ровно ∣V∣−1|V|-1∣V∣−1 раз. После этого расстояния корректны; дополнительная итерация детектирует отриц. цикл, если какие‑то ddd изменились.
- Псевдокод (сжато): для i=1i=1i=1 до ∣V∣−1|V|-1∣V∣−1: для каждого ребра (u,v)(u,v)(u,v) если d(u)+w(u,v)<d(v)d(u)+w(u,v)<d(v)d(u)+w(u,v)<d(v) то d(v)=d(u)+w(u,v)d(v)=d(u)+w(u,v)d(v)=d(u)+w(u,v). После: ещё одна итерация — если есть улучшение, то есть отриц. цикл.
- Сложность: O(∣V∣⋅∣E∣)\mathcal{O}(|V|\cdot|E|)O(∣V∣⋅∣E∣) по времени, O(∣V∣+∣E∣)\mathcal{O}(|V|+|E|)O(∣V∣+∣E∣) по памяти.
- Применимость: надежен для single‑source в графах с отриц. ребрами; обнаруживает отриц. циклы.
2) Dijkstra напрямую — нельзя, если есть отриц. ребра (жадность нарушается).
- Пример провала: наличие отрицательного ребра может сделать уже «закрытое» расстояние улучшенным через более длинный путь.
3) Johnson (переход к неотрицательным весам) — когда нужно all‑pairs или хочется использовать Dijkstra после пересчёта
- Шаги:
1. Добавить вспомогательную вершину s′s's′ со сведениями w(s′,v)=0w(s',v)=0w(s′,v)=0 для всех vvv.
2. Запустить Bellman–Ford от s′s's′ и получить потенциалы h(v)=δ(s′,v)h(v)=\delta(s',v)h(v)=δ(s′,v). Если найден отриц. цикл — останов.
3. Переопределить веса: w′(u,v)=w(u,v)+h(u)−h(v)w'(u,v)=w(u,v)+h(u)-h(v)w′(u,v)=w(u,v)+h(u)−h(v). Тогда все w′≥0w'\ge 0w′≥0.
4. Для каждого источника sss запустить Dijkstra с весами w′w'w′, получив d′(v)d'(v)d′(v). Восстановить оригинальные расстояния: d(v)=d′(v)−h(s)+h(v)d(v)=d'(v)-h(s)+h(v)d(v)=d′(v)−h(s)+h(v).
- Сложность: Bellman–Ford O(∣V∣⋅∣E∣)\mathcal{O}(|V|\cdot|E|)O(∣V∣⋅∣E∣) + для каждого источника Dijkstra (с кучей) O(∣E∣+∣V∣log∣V∣)\mathcal{O}(|E|+|V|\log|V|)O(∣E∣+∣V∣log∣V∣). Для all‑pairs итог: O(∣V∣⋅∣E∣+∣V∣2log∣V∣)\mathcal{O}(|V|\cdot|E| + |V|^2\log|V|)O(∣V∣⋅∣E∣+∣V∣2log∣V∣) (на практике: O(∣V∣⋅∣E∣+∣V∣⋅(∣E∣+∣V∣log∣V∣))\mathcal{O}(|V|\cdot|E| + |V|\cdot(|E|+|V|\log|V|))O(∣V∣⋅∣E∣+∣V∣⋅(∣E∣+∣V∣log∣V∣))).
- Применимость: эффективно для all‑pairs в разреженных графах; для single‑source обычно проще Bellman–Ford (если нет других причин).
4) SPFA (очередь релаксаций) — практический эвристический вариант Bellman–Ford
- Часто быстрее на реальных данных, но в худшем случае O(∣V∣⋅∣E∣)\mathcal{O}(|V|\cdot|E|)O(∣V∣⋅∣E∣) (и в некоторых конструкциях — крайне плохой) — использовать осторожно.
Рекомендации выбора
- Single‑source + отриц. рёбра (и нужно детектировать отриц. цикл) — Bellman–Ford.
- All‑pairs + отриц. рёбра (нет отриц. циклов) — Johnson (BF для потенциалов + Dijkstra для каждого источника).
- Нет отриц. рёбер — Dijkstra.
- Практическая альтернатива для Single‑source: SPFA (быстро обычно, но не гарантированно).
Короткое обоснование корректности Bellman–Ford: кратчайший простой путь содержит не более ∣V∣−1|V|-1∣V∣−1 рёбер, поэтому после ∣V∣−1|V|-1∣V∣−1 полных проходов по всем рёбрам все такие пути будут учтены; дополнительная итерация показывает наличие отриц. цикла.