Алгоритм поиска блокирующего потока в ациклической сети — различия между версиями
(→Источники) |
6yry6e (обсуждение | вклад) (изменен псевдокод и исправлены опечатки) |
||
| Строка 1: | Строка 1: | ||
| − | ==Жадный | + | ==Жадный алгоритм== |
Идея заключается в том, чтобы по одному находить пути из [[Определение_сети,_потока|истока]] <tex>s</tex> в [[Определение_сети,_потока|сток]] <tex>t</tex>, пока это возможно. [[Обход в глубину, цвета вершин| Обход в глубину]] найдёт все пути из <tex>s</tex> в <tex>t</tex>, если из <tex>s</tex> достижима <tex>t</tex>, а [[Определение_сети,_потока|пропускная способность]] каждого ребра <tex>c(u, v)>0</tex> поэтому, насыщая рёбра, мы хотя бы единожды достигнем стока <tex>t</tex>, следовательно блокирующий поток всегда найдётся. | Идея заключается в том, чтобы по одному находить пути из [[Определение_сети,_потока|истока]] <tex>s</tex> в [[Определение_сети,_потока|сток]] <tex>t</tex>, пока это возможно. [[Обход в глубину, цвета вершин| Обход в глубину]] найдёт все пути из <tex>s</tex> в <tex>t</tex>, если из <tex>s</tex> достижима <tex>t</tex>, а [[Определение_сети,_потока|пропускная способность]] каждого ребра <tex>c(u, v)>0</tex> поэтому, насыщая рёбра, мы хотя бы единожды достигнем стока <tex>t</tex>, следовательно блокирующий поток всегда найдётся. | ||
| Строка 5: | Строка 5: | ||
==Удаляющий обход== | ==Удаляющий обход== | ||
| − | Аналогично предыдущей идее, однако будем удалять в процессе обхода в глубину из графа все рёбра, вдоль которых не получится дойти до стока <tex>t</tex>. Это очень легко реализовать: достаточно удалять ребро после того, как мы просмотрели его в обходе в глубину (кроме того случая, когда мы прошли вдоль ребра и нашли путь до стока). С точки зрения реализации, надо просто поддерживать в списке смежности каждой вершины указатель на первое | + | Аналогично предыдущей идее, однако будем удалять в процессе обхода в глубину из графа все рёбра, вдоль которых не получится дойти до стока <tex>t</tex>. Это очень легко реализовать: достаточно удалять ребро после того, как мы просмотрели его в обходе в глубину (кроме того случая, когда мы прошли вдоль ребра и нашли путь до стока). С точки зрения реализации, надо просто поддерживать в списке смежности каждой вершины указатель на первое не удалённое ребро, и увеличивать этот указать в цикле внутри обхода в глубину. Корректность при этом сохраняется согласно предыдущему пункту. |
| − | dfs() | + | '''dfs''' (''v'', ''flow'') |
| − | + | '''if''' (''flow'' == 0) | |
| − | + | '''return''' 0; | |
| − | + | '''if''' (''v'' == '''t''') | |
| − | + | '''return''' ''flow''; | |
| − | + | '''for''' (''u'' = ptr[''v''] '''to''' n) // ''u'' is reference value | |
| − | + | '''if''' (''vu'' <tex>\in E</tex>) | |
| − | + | pushed = '''dfs''' (''u'', min (flow, c(''vu'') - f(''vu''))); | |
| − | + | f(''vu'') += pushed; | |
| − | + | f(''uv'') -= pushed; | |
| − | + | '''return''' pushed; | |
| − | + | '''return''' 0; | |
| − | + | ||
| − | + | '''main''' ( ) | |
| − | + | '''...''' | |
| − | + | ''flow'' = 0; | |
| − | + | ptr[''i''] = 0, ''i'' = ''1...n''; | |
| − | + | '''while''' (pushed = '''dfs''' ('''s''', ''INF'')) | |
| − | + | ''flow'' += pushed; | |
| − | |||
| − | dfs(); | ||
Если обход в глубину достигает стока, насыщается как минимум одно ребро, иначе как минимум один указатель продвигается вперед. Значит один запуск обхода в глубину работает за <tex>O(V + K)</tex>, где <tex>V</tex> — число вершин в графе, а <tex>K</tex> — число продвижения указателей. Ввиду того, что всего запусков обхода в глубину в рамках поиска одного [[Блокирующий поток|блокирующего потока]] будет <tex>O(P)</tex>, где <tex>P</tex> — число рёбер, насыщенных этим блокирующим потоком, то весь алгоритм поиска блокирующего потока отработает за <tex>O(PV + \sum\limits_i{K_i})</tex>, что, учитывая, что все указатели в сумме прошли расстояние <tex>O(E)</tex>, дает асимптотику <tex>O(PV + E)</tex>. В худшем случае, когда блокирующий поток насыщает все ребра, асимптотика получается <tex>O(VE)</tex>. | Если обход в глубину достигает стока, насыщается как минимум одно ребро, иначе как минимум один указатель продвигается вперед. Значит один запуск обхода в глубину работает за <tex>O(V + K)</tex>, где <tex>V</tex> — число вершин в графе, а <tex>K</tex> — число продвижения указателей. Ввиду того, что всего запусков обхода в глубину в рамках поиска одного [[Блокирующий поток|блокирующего потока]] будет <tex>O(P)</tex>, где <tex>P</tex> — число рёбер, насыщенных этим блокирующим потоком, то весь алгоритм поиска блокирующего потока отработает за <tex>O(PV + \sum\limits_i{K_i})</tex>, что, учитывая, что все указатели в сумме прошли расстояние <tex>O(E)</tex>, дает асимптотику <tex>O(PV + E)</tex>. В худшем случае, когда блокирующий поток насыщает все ребра, асимптотика получается <tex>O(VE)</tex>. | ||
| − | <b>Замечание</b> Если в [[Схема алгоритма Диница|алгоритме Диница]] искать блокирующий поток удаляющим обходом, то его эффективность составит <tex>O(V^2E)</tex>, что уже лучше эффективности [[Алоритм Эдмондса-Карпа|алгоритма Эдмондса-Карпа]] <tex>O(VE^2)</tex>. | + | <b>Замечание:</b> Если в [[Схема алгоритма Диница|алгоритме Диница]] искать блокирующий поток удаляющим обходом, то его эффективность составит <tex>O(V^2E)</tex>, что уже лучше эффективности [[Алоритм Эдмондса-Карпа|алгоритма Эдмондса-Карпа]] <tex>O(VE^2)</tex>. |
==Алгоритм Малхотры — Кумара — Махешвари== | ==Алгоритм Малхотры — Кумара — Махешвари== | ||
| Строка 57: | Строка 55: | ||
while (<tex>t</tex> достижима из <tex>s</tex> в <tex>L</tex>) | while (<tex>t</tex> достижима из <tex>s</tex> в <tex>L</tex>) | ||
{ | { | ||
| − | найдём <tex>v</tex> с | + | найдём <tex>v</tex> с минимальной пропускной способностью <tex>g</tex>; |
проталкиваем <tex>g</tex> единиц потока из <tex>v</tex> в <tex>t</tex>; | проталкиваем <tex>g</tex> единиц потока из <tex>v</tex> в <tex>t</tex>; | ||
проталкиваем <tex>g</tex> единиц потока из <tex>s</tex> в <tex>v</tex>; | проталкиваем <tex>g</tex> единиц потока из <tex>s</tex> в <tex>v</tex>; | ||
Версия 22:32, 28 января 2016
Содержание
Жадный алгоритм
Идея заключается в том, чтобы по одному находить пути из истока в сток , пока это возможно. Обход в глубину найдёт все пути из в , если из достижима , а пропускная способность каждого ребра поэтому, насыщая рёбра, мы хотя бы единожды достигнем стока , следовательно блокирующий поток всегда найдётся.
Используя , каждый путь находится за , где — число рёбер в графе. Поскольку каждый путь насыщает как минимум одно ребро, всего будет путей. Итого общая асимптотика составляет .
Удаляющий обход
Аналогично предыдущей идее, однако будем удалять в процессе обхода в глубину из графа все рёбра, вдоль которых не получится дойти до стока . Это очень легко реализовать: достаточно удалять ребро после того, как мы просмотрели его в обходе в глубину (кроме того случая, когда мы прошли вдоль ребра и нашли путь до стока). С точки зрения реализации, надо просто поддерживать в списке смежности каждой вершины указатель на первое не удалённое ребро, и увеличивать этот указать в цикле внутри обхода в глубину. Корректность при этом сохраняется согласно предыдущему пункту.
dfs (v, flow)
if (flow == 0)
return 0;
if (v == t)
return flow;
for (u = ptr[v] to n) // u is reference value
if (vu )
pushed = dfs (u, min (flow, c(vu) - f(vu)));
f(vu) += pushed;
f(uv) -= pushed;
return pushed;
return 0;
main ( )
...
flow = 0;
ptr[i] = 0, i = 1...n;
while (pushed = dfs (s, INF))
flow += pushed;
Если обход в глубину достигает стока, насыщается как минимум одно ребро, иначе как минимум один указатель продвигается вперед. Значит один запуск обхода в глубину работает за , где — число вершин в графе, а — число продвижения указателей. Ввиду того, что всего запусков обхода в глубину в рамках поиска одного блокирующего потока будет , где — число рёбер, насыщенных этим блокирующим потоком, то весь алгоритм поиска блокирующего потока отработает за , что, учитывая, что все указатели в сумме прошли расстояние , дает асимптотику . В худшем случае, когда блокирующий поток насыщает все ребра, асимптотика получается .
Замечание: Если в алгоритме Диница искать блокирующий поток удаляющим обходом, то его эффективность составит , что уже лучше эффективности алгоритма Эдмондса-Карпа .
Алгоритм Малхотры — Кумара — Махешвари
Идея
Для каждой вершины вводится потенциал потока, равный максимальному дополнительному потоку, который может пройти через эту вершину. Далее запускаем цикл, на каждой итерации которого определяем вершину с минимальным потенциалом . Затем пускается поток величины из истока в сток, проходящий через эту вершину. При этом если остаточная пропускная способность ребра равна нулю, то это ребро удаляется. Также, удаляются все вершины, у которых не остаётся ни одного входящего и/или ни одного выходящего ребра. При удалении вершины все смежные ребра удаляются.
Подробное описание
- Для каждой вершины вычислим входящий и исходящий потенциал: и . Пусть и . Определим потенциал или пропускную способность вершины в сети . Таким образом, потенциал вершины определяет максимально возможное количество потока, который может через нее проходить. Ясно, что через вершины с поток проходить не может. Следовательно, их можно удалить из вспомогательной сети. Удалим эти вершины и дуги, им инцидентные, обновив должным образом потенциалы вершин, смежных с удаленными. Если в результате появятся новые вершины с , удалим рекурсивно и их. В результате во вспомогательной сети останутся только вершины с .
- После этого приступим к построению блокирующего потока. Пусть вершина принадлежит -ому слою и , где — -й слой. Протолкнем единиц потока из вершины в смежные с ней вершины по исходящим дугам с остаточной пропускной способностью . Попутно будем переносить проталкиваемый поток в исходную сеть, а также корректировать потенциалы вершин, отправляющих и принимающих избыток потока. В результате, весь (в виду минимальности потенциала вершины ) проталкиваемый поток соберется в вершинах -го слоя.
- Повторим процесс отправки потока из вершин -го слоя, содержащих избыток потока, в смежные им вершины -го слоя. И так до тех пор, пока весь поток не соберется в последнем слое, в котором содержится только сток , ибо все остальные вершины, ранее ему принадлежащие, были удалены, поскольку их потенциалы нулевые. Следовательно, весь поток величины , отправленный из вершины , где - минимальный полностью соберется в .
- На втором этапе вновь, начиная с вершины , осуществляется подвод потока уже по входящим дугам. В результате на первом шаге недостаток потока переадресуется к узлам -го слоя, затем -го. И так до тех пор, пока весь потока величины , отправленные из вершины , где - минимальный, не соберется в истоке . Таким образом, поток и во вспомогательной и в основной сети увеличится на величину .
MPM algorithm() { foreach ; Вычисляем остаточную сеть ; Найдём вспомогательный граф для ; while () { while ( достижима из в ) { найдём с минимальной пропускной способностью ; проталкиваем единиц потока из в ; проталкиваем единиц потока из в ; изменяем , и ; } вычисляем новый вспомогательный граф из ; } }
Асимптотика
Если информация о входящих и исходящих дугах будет храниться в виде связных списков, то для того, чтобы пропустить поток, на каждой итерации будет выполнено действий, где соответствует числу рёбер, для которых остаточная пропускная способность уменьшилась, но осталась положительной, а — числу удалённых ребер. Таким образом, для поиска блокирующего потока будет выполнено действий.