Алгоритм поиска блокирующего потока в ациклической сети — различия между версиями
6yry6e (обсуждение | вклад) (изменен псевдокод и исправлены опечатки) |
6yry6e (обсуждение | вклад) (оформление) |
||
| Строка 7: | Строка 7: | ||
Аналогично предыдущей идее, однако будем удалять в процессе обхода в глубину из графа все рёбра, вдоль которых не получится дойти до стока <tex>t</tex>. Это очень легко реализовать: достаточно удалять ребро после того, как мы просмотрели его в обходе в глубину (кроме того случая, когда мы прошли вдоль ребра и нашли путь до стока). С точки зрения реализации, надо просто поддерживать в списке смежности каждой вершины указатель на первое не удалённое ребро, и увеличивать этот указать в цикле внутри обхода в глубину. Корректность при этом сохраняется согласно предыдущему пункту. | Аналогично предыдущей идее, однако будем удалять в процессе обхода в глубину из графа все рёбра, вдоль которых не получится дойти до стока <tex>t</tex>. Это очень легко реализовать: достаточно удалять ребро после того, как мы просмотрели его в обходе в глубину (кроме того случая, когда мы прошли вдоль ребра и нашли путь до стока). С точки зрения реализации, надо просто поддерживать в списке смежности каждой вершины указатель на первое не удалённое ребро, и увеличивать этот указать в цикле внутри обхода в глубину. Корректность при этом сохраняется согласно предыдущему пункту. | ||
| − | '''dfs''' | + | '''int''' dfs('''int''' <tex>v</tex>, '''int''' flow) |
| − | '''if''' ( | + | '''if''' (flow == 0) |
| − | '''return''' 0 | + | '''return''' 0 |
| − | '''if''' ( | + | '''if''' (<tex>v</tex> == <tex>t</tex>) |
| − | '''return | + | '''return''' flow |
| − | '''for''' ( | + | '''for''' (<tex>u</tex> = ptr[<tex>v</tex>] '''to''' n) <font color=darkgreen> // u - ссылочный тип</font> |
| − | '''if''' ( | + | '''if''' (<tex>vu \in E</tex>) |
| − | pushed = | + | pushed = dfs(<tex>u</tex>, min(flow, c(<tex>vu</tex>) - f(<tex>vu</tex>))); |
| − | f( | + | f(<tex>vu</tex>) += pushed |
| − | f( | + | f(<tex>uv</tex>) -= pushed |
| − | '''return''' pushed | + | '''return''' pushed |
| − | '''return''' 0 | + | '''return''' 0 |
| − | '''main''' ( ) | + | '''main'''( ) |
'''...''' | '''...''' | ||
| − | + | flow = 0 | |
| − | + | '''for''' ('''int''' i = 0 '''to''' n) | |
| − | ''' | + | ptr[i] = 0; |
| − | '' | + | '''do''' |
| + | pushed = '''dfs''' (<tex>s</tex>, <tex>\infty</tex>) | ||
| + | flow += pushed; | ||
| + | '''while''' (pushed > 0) | ||
| + | |||
Если обход в глубину достигает стока, насыщается как минимум одно ребро, иначе как минимум один указатель продвигается вперед. Значит один запуск обхода в глубину работает за <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>. | ||
| Строка 67: | Строка 71: | ||
Если информация о входящих и исходящих дугах будет храниться в виде связных списков, то для того, чтобы пропустить поток, на каждой итерации будет выполнено <tex>O(K + E_i)</tex> действий, где <tex>K</tex> соответствует числу рёбер, для которых остаточная пропускная способность уменьшилась, но осталась положительной, а <tex>E_i</tex> — числу удалённых ребер. Таким образом, для поиска блокирующего потока будет выполнено <tex>\sum\limits_i{O(K+E_i)} = O(K^2)</tex> действий. | Если информация о входящих и исходящих дугах будет храниться в виде связных списков, то для того, чтобы пропустить поток, на каждой итерации будет выполнено <tex>O(K + E_i)</tex> действий, где <tex>K</tex> соответствует числу рёбер, для которых остаточная пропускная способность уменьшилась, но осталась положительной, а <tex>E_i</tex> — числу удалённых ребер. Таким образом, для поиска блокирующего потока будет выполнено <tex>\sum\limits_i{O(K+E_i)} = O(K^2)</tex> действий. | ||
| − | ==Источники== | + | == См. также == |
| − | *[http://e-maxx.ru/algo/dinic | + | * [[Блокирующий поток]] |
| + | * [[Схема алгоритма Диница]] | ||
| + | |||
| + | ==Источники информации== | ||
| + | *[http://www.e-maxx.ru/algo/dinic Алгоритм Диница. Необходимые определения.] | ||
*[http://www.facweb.iitkgp.ernet.in/~arijit/courses/autumn2006/cs60001/lec-flow-4.pdf The MPM Algorithm] | *[http://www.facweb.iitkgp.ernet.in/~arijit/courses/autumn2006/cs60001/lec-flow-4.pdf The MPM Algorithm] | ||
*[http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9C%D0%B0%D0%BB%D1%85%D0%BE%D1%82%D1%80%D1%8B_%E2%80%94_%D0%9A%D1%83%D0%BC%D0%B0%D1%80%D0%B0_%E2%80%94_%D0%9C%D0%B0%D1%85%D0%B5%D1%88%D0%B2%D0%B0%D1%80%D0%B8 Алгоритм Малхотры — Кумара — Махешвари] | *[http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9C%D0%B0%D0%BB%D1%85%D0%BE%D1%82%D1%80%D1%8B_%E2%80%94_%D0%9A%D1%83%D0%BC%D0%B0%D1%80%D0%B0_%E2%80%94_%D0%9C%D0%B0%D1%85%D0%B5%D1%88%D0%B2%D0%B0%D1%80%D0%B8 Алгоритм Малхотры — Кумара — Махешвари] | ||
Версия 23:08, 28 января 2016
Содержание
Жадный алгоритм
Идея заключается в том, чтобы по одному находить пути из истока в сток , пока это возможно. Обход в глубину найдёт все пути из в , если из достижима , а пропускная способность каждого ребра поэтому, насыщая рёбра, мы хотя бы единожды достигнем стока , следовательно блокирующий поток всегда найдётся.
Используя , каждый путь находится за , где — число рёбер в графе. Поскольку каждый путь насыщает как минимум одно ребро, всего будет путей. Итого общая асимптотика составляет .
Удаляющий обход
Аналогично предыдущей идее, однако будем удалять в процессе обхода в глубину из графа все рёбра, вдоль которых не получится дойти до стока . Это очень легко реализовать: достаточно удалять ребро после того, как мы просмотрели его в обходе в глубину (кроме того случая, когда мы прошли вдоль ребра и нашли путь до стока). С точки зрения реализации, надо просто поддерживать в списке смежности каждой вершины указатель на первое не удалённое ребро, и увеличивать этот указать в цикле внутри обхода в глубину. Корректность при этом сохраняется согласно предыдущему пункту.
int dfs(int , int flow) if (flow == 0) return 0 if ( == ) return flow for ( = ptr[] to n) // u - ссылочный тип if () pushed = dfs(, min(flow, c() - f())); f() += pushed f() -= pushed return pushed return 0
main( )
...
flow = 0
for (int i = 0 to n)
ptr[i] = 0;
do
pushed = dfs (, )
flow += pushed;
while (pushed > 0)
Если обход в глубину достигает стока, насыщается как минимум одно ребро, иначе как минимум один указатель продвигается вперед. Значит один запуск обхода в глубину работает за , где — число вершин в графе, а — число продвижения указателей. Ввиду того, что всего запусков обхода в глубину в рамках поиска одного блокирующего потока будет , где — число рёбер, насыщенных этим блокирующим потоком, то весь алгоритм поиска блокирующего потока отработает за , что, учитывая, что все указатели в сумме прошли расстояние , дает асимптотику . В худшем случае, когда блокирующий поток насыщает все ребра, асимптотика получается .
Замечание: Если в алгоритме Диница искать блокирующий поток удаляющим обходом, то его эффективность составит , что уже лучше эффективности алгоритма Эдмондса-Карпа .
Алгоритм Малхотры — Кумара — Махешвари
Идея
Для каждой вершины вводится потенциал потока, равный максимальному дополнительному потоку, который может пройти через эту вершину. Далее запускаем цикл, на каждой итерации которого определяем вершину с минимальным потенциалом . Затем пускается поток величины из истока в сток, проходящий через эту вершину. При этом если остаточная пропускная способность ребра равна нулю, то это ребро удаляется. Также, удаляются все вершины, у которых не остаётся ни одного входящего и/или ни одного выходящего ребра. При удалении вершины все смежные ребра удаляются.
Подробное описание
- Для каждой вершины вычислим входящий и исходящий потенциал: и . Пусть и . Определим потенциал или пропускную способность вершины в сети . Таким образом, потенциал вершины определяет максимально возможное количество потока, который может через нее проходить. Ясно, что через вершины с поток проходить не может. Следовательно, их можно удалить из вспомогательной сети. Удалим эти вершины и дуги, им инцидентные, обновив должным образом потенциалы вершин, смежных с удаленными. Если в результате появятся новые вершины с , удалим рекурсивно и их. В результате во вспомогательной сети останутся только вершины с .
- После этого приступим к построению блокирующего потока. Пусть вершина принадлежит -ому слою и , где — -й слой. Протолкнем единиц потока из вершины в смежные с ней вершины по исходящим дугам с остаточной пропускной способностью . Попутно будем переносить проталкиваемый поток в исходную сеть, а также корректировать потенциалы вершин, отправляющих и принимающих избыток потока. В результате, весь (в виду минимальности потенциала вершины ) проталкиваемый поток соберется в вершинах -го слоя.
- Повторим процесс отправки потока из вершин -го слоя, содержащих избыток потока, в смежные им вершины -го слоя. И так до тех пор, пока весь поток не соберется в последнем слое, в котором содержится только сток , ибо все остальные вершины, ранее ему принадлежащие, были удалены, поскольку их потенциалы нулевые. Следовательно, весь поток величины , отправленный из вершины , где - минимальный полностью соберется в .
- На втором этапе вновь, начиная с вершины , осуществляется подвод потока уже по входящим дугам. В результате на первом шаге недостаток потока переадресуется к узлам -го слоя, затем -го. И так до тех пор, пока весь потока величины , отправленные из вершины , где - минимальный, не соберется в истоке . Таким образом, поток и во вспомогательной и в основной сети увеличится на величину .
MPM algorithm() { foreach ; Вычисляем остаточную сеть ; Найдём вспомогательный граф для ; while () { while ( достижима из в ) { найдём с минимальной пропускной способностью ; проталкиваем единиц потока из в ; проталкиваем единиц потока из в ; изменяем , и ; } вычисляем новый вспомогательный граф из ; } }
Асимптотика
Если информация о входящих и исходящих дугах будет храниться в виде связных списков, то для того, чтобы пропустить поток, на каждой итерации будет выполнено действий, где соответствует числу рёбер, для которых остаточная пропускная способность уменьшилась, но осталась положительной, а — числу удалённых ребер. Таким образом, для поиска блокирующего потока будет выполнено действий.