Рассмотрите следующий псевдокод Dijkstra, который используется на графе с возможными отрицательными весами: initialize distances; while unvisited exist pick min dist u; for each v adjacent to u if dist[v] > dist[u] + w(u,v) then dist[v] = dist[u] + w(u,v); Объясните, почему этот подход даёт неверные результаты при отрицательных ребрах, приведите корректные альтернативы (алгоритмы и методы преобразования графа) и проанализируйте их сложность и применимость

22 Окт в 14:50
4 +1
0
Ответы
1
Кратко — почему неверно, демонстрация и корректные заменители.
Почему жадный Dijkstra ломается при отрицательных ребрах
- Ключевое свойство Dijkstra: если в очереди необработанных вершин вершина uuu имеет минимальную текущую оценку dist⁡[u]\operatorname{dist}[u]dist[u], то эта оценка уже равна истинному кратчайшему расстоянию от источника. Это верно при неотрицательных весах, потому что любой путь, проходящий через ещё не обработанную вершину, не может уменьшить длину (ребро добавляет неотрицательное значение).
- При отрицательных ребрах это свойство нарушается: может существовать путь через ещё не обработанные вершины, который уменьшит dist⁡[u]\operatorname{dist}[u]dist[u].
Простой контрпример
- Граф: вершины s,a,bs,a,bs,a,b. Рёбра: s→as\to asa вес 000, s→bs\to bsb вес 111, b→ab\to aba вес −2-22.
- Инициализация: dist⁡[s]=0, dist⁡[a]=0, dist⁡[b]=1\operatorname{dist}[s]=0,\ \operatorname{dist}[a]=0,\ \operatorname{dist}[b]=1dist[s]=0, dist[a]=0, dist[b]=1.
- Dijkstra выберет минимальную необработанную вершину aaa (значение 000) и "зафиксирует" её. Но истинный кратчайший путь s→b→as\to b\to asba имеет длину 1+(−2)=−1<01 + (-2) = -1 < 01+(2)=1<0. После фиксации aaa её значение не изменится — алгоритм даёт неверный результат.
Почему математически: при отрицательных ребрах условие монотонности нарушено — возможные улучшения через необработанные вершины не гарантированно не уменьшают текущие метки.
Корректные альтернативы и преобразования (кратко, с оценками сложности)
1) Bellman–Ford (одиночный источник, допускает отрицательные веса; обнаруживает отрицательные циклы)
- Описание: релаксировать все ребра до V−1V-1V1 раз; затем дополнительный проход для обнаружения отрицательного цикла.
- Сложность: O(VE)\;O(VE)O(VE).
- Применимость: одиночный источник, работает при любых весах (кроме случаев, когда интересна неограниченно малая длина из-за отрицательного цикла — тогда задача не имеет смысла).
2) SPFA (Shortest Path Faster Algorithm)
- Описание: оптимизация Bellman–Ford с очередью (релаксация только нужных ребер).
- Практика: часто быстрее на разреженных реальных графах.
- Сложность: в худшем случае асимптотика сравнима с Bellman–Ford (O(VE)O(VE)O(VE)), на специальных случаях может деградировать; не гарантированно лучше в теории.
- Применимость: практический выбор, но осторожно на анти-паттерн графах.
3) Алгоритм для DAG (если граф ацикличен)
- Описание: топологическая сортировка + однократная релаксация рёбер в порядке топологической сортировки.
- Сложность: O(V+E)\;O(V+E)O(V+E).
- Применимость: подходит, если граф ориентированный и ацикличен; допускает отрицательные веса.
4) Johnson — все-пары кратчайших путей с переквотировкой (reweighting)
- Идея: добавить фиктивный источник s′s's с нулевыми рёбрами ко всем вершинам, запустить Bellman–Ford от s′s's и получить потенциалы h(v)=dist⁡(s′,v)h(v)=\operatorname{dist}(s',v)h(v)=dist(s,v). Затем переопределить веса
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′w'w неотрицательны, и кратчайшие пути сохраняются по порядку. После этого запускать Dijkstra из каждой вершины по w′w'w и переводить ответы обратно.
- Сложность: Bellman–Ford за O(VE)O(VE)O(VE) + VVV запусков Dijkstra. Итог (зависит от реализации Dijkstra):
- с Fibonacci-кучей: O(VE+V2log⁡V)\;O(VE + V^2\log V)O(VE+V2logV);
- с бинарной кучей: примерно O(VElog⁡V)\;O(VE\log V)O(VElogV) при доминировании EEE.
- Применимость: когда нужно найти все-пары кратчайшие пути в графе с отрицательными, но не циклически отрицательными весами.
5) Прямое исправление путём добавления константы ко всем рёбрам — НЕ корректно
- Если добавить постоянную CCC к каждому весу, то длина пути с kkk рёбрами изменится на kCkCkC. Порядок предпочтения путей может измениться (короткий по сумме может иметь больше/меньше рёбер), поэтому это не сохраняет корректность для всех пар вершин.
Дополнительные замечания
- Если в графе есть достижимый отрицательный цикл, коротчайшие пути (по сумме весов) не определены (можно сделать длину сколь угодно малой) — Bellman–Ford и SPFA умеют это обнаруживать.
- Если требуются кратчайшие пути от одного источника в общем графе с отрицательными весами — используйте Bellman–Ford (гарантия) или SPFA (практически быстрее, но с оговорками). Если требуется все-пары — Johnson предпочтителен (когда VVV не слишком велик и EEE разрежён) или квадратный алгоритм Floyd–Warshall (O(V3)O(V^3)O(V3)) как простой вариант (подходит при плотных графах и небольших VVV).
Резюме
- Приведённый псевдокод Dijkstra неверен при отрицательных рёбрах потому, что жадное утверждение о "финальности" минимальной текущей метки нарушается.
- Правильные методы: Bellman–Ford (S S P с отрицательными весами), SPFA (практическая оптимизация), алгоритм для DAG (если ацикличен), Johnson (для all-pairs с переквотировкой), Floyd–Warshall (O(V3)O(V^3)O(V3)) — выбор зависит от задачи и размеров графа.
22 Окт в 15:21
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир