Изменения

Перейти к: навигация, поиск

Алгоритм "поднять-в-начало"

1239 байт добавлено, 01:58, 5 января 2016
Источники информации
'''Алгоритм "поднять-в-начало" ''' (англ. ''relabel-to-front)''' ) основан на [[Метод проталкивания предпотока|методе проталкивание предпотока]], но из-за тщательного выбора порядка выполнения операций [[Метод проталкивания предпотока#Проталкивание (push)|проталкивания]] и [[Метод проталкивания предпотока#Подъем (relabel)|подъема]], время выполнения данного алгоритма составляет <tex>O(V^{3})</tex>, что асимптотически не хуже, чем <tex>O(V^{2}E)</tex>.
== Допустимые ребра ==
{{Определение
|definition=
'''Допустимое ребро ''' (англ. ''admissible edge)''' ) {{---}} ребро <tex>uv(u, v)</tex>, у которого <tex>c_{f}(u, v) > 0</tex> и <tex>h[u] = h[v] + 1</tex>. В противном случае <tex>uv(u, v)</tex> называется '''недопустимым ''' (англ. ''inadmissible)''').
}}
{{Определение
|definition=
'''Допустимая сеть ''' (англ. ''admissible network)''' ) {{---}} сеть <tex>G_{f, h} = (V, E_{f, h})</tex>, где <tex>E_{f, h}</tex> {{---}} множество допустимых ребер.
}}
== Операция разгрузки (discharge) ==
{{Определение
|definition=
'''Разгрузка (discharge)''' {{---}} операция, применяемая к переполненной вершине <tex>u</tex>, для того чтобы протолкнуть поток через допустимые ребра в смежные вершины, при необходимости поднимая <tex>u</tex>, делая недопустимые ребра, выходящие из вершины <tex>u</tex>, допустимыми.
}}
Будем хранить для каждой вершины <tex>u</tex> список <tex>N[u]</tex> (список вершин, смежных с ней). То есть список <tex>N[u]</tex> содержит каждую вершину <tex>v</tex> такую, что в сети <tex>G = (V, E) ~ (u, v) \in E</tex> или <tex>(v, u) \in E</tex>.
'''Разгрузка''' (англ. ''discharge'') {{---}} операция, применяемая к переполненной вершине <tex>u</tex>, для того чтобы протолкнуть поток через допустимые ребра в смежные вершины, при необходимости поднимая <tex>u</tex>, делая недопустимые ребра, выходящие из вершины <tex>u</tex>, допустимыми. Будем хранить для каждой вершины <tex>u</tex> список <tex>N[u]</tex> (список вершин, смежных с ней). То есть список <tex>N[u]</tex> содержит каждую вершину <tex>v</tex> такую, что в сети <tex>G = (V, E)</tex> ребро <tex>(u, v) \in E</tex> или <tex>(v, u) \in E</tex>.  На первую вершину в списке указывает указатель <tex>head[N[u]]</tex>. Для перехода к следующей вершине в списке за <tex>w</tex>, поддерживается указатель <tex>next[w]</tex>. Он равен <tex>null\varnothing</tex>, если <tex>w</tex> {{---}} последняя вершина в списке.
Для каждой вершины <tex>u</tex> указатель <tex>current[u]</tex> {{---}} указатель на текущую вершину списка. Изначально <tex>current[u] = head[N[u]]</tex>.
'''dischargefunction'''<tex>\mathtt{discharge}(u):</tex> '''while''' <tex>e[u] > 0</tex> <tex>v = current[u]</tex> '''if''' <tex>v = null \varnothing</tex> <tex>\mathtt{relabel}(u)</tex> <tex>current[u] = head[N[u]]</tex>
'''else'''
'''if''' <tex>c(u, v) - f(u, v) > 0 </tex> '''and ''' <tex>h[u] = h[v] + 1</tex> <tex>\mathtt{push}(u, v)</tex>
'''else'''
<tex>current[u] = next[v]</tex>
Операция завершится только тогда, когда избыток <tex>e(u)</tex> станет равным нулю, и ни подъем, ни перемещение указателя <tex>current[u]</tex> не влияет на значение <tex>e(u)</tex>.
Докажем то, что когда операция '''<tex>\mathtt{discharge''' }</tex> вызывает операции [[Метод проталкивания предпотока#Проталкивание (push)|push]] и [[Метод проталкивания предпотока#Подъем (relabel)|relable]], эти операции они применимы.
|id = Лемма4
|statement =
Когда операция '''<tex>\mathtt{discharge''' }</tex> вызывает в операцию <tex>push(u, v)</tex>, то для пары вершин <tex>(u, v)</tex> применима операция проталкивания.
|proof =
Проверки операции '''<tex>\mathtt{discharge'''}</tex>, сделанные до вызова операции проталкивания, гарантируют то, что операция <tex>push</tex> будет вызвана только тогда, когда она применима. То есть <tex>e(u) > 0</tex>, <tex>c_{f}(u, v) > 0</tex> и <tex>h(u) = h(v) + 1</tex>.
}}
|id = Лемма5
|statement =
Когда операция '''<tex>\mathtt{discharge''' }</tex> вызывает в операцию <tex>relabel(u)</tex>, то для вершины <tex>u</tex> применим подъем.
|proof =
Из [[Алгоритм "поднять-в-начало"#Лемма3|леммы об изменении допустимой цепи (для операции relabel)]] и условия <tex>e(u) > 0</tex> следует, что для доказательства данной леммы необходимо показать, что все ребра , выходящие из <tex>u</tex>, являются недопустимыми.
Каждый проход операции '''<tex>\mathtt{discharge}(u)''' </tex> начинается с головы списка <tex>N[u]</tex> и оканчивается, когда <tex>current[u] = null \varnothing</tex>. Именно тогда вызывается <tex>relabel(u)</tex> , и начинается новый проход. К концу прохода все ребра, выходящие из <tex>u</tex>, станут недопустимыми, так как из [[Алгоритм "поднять-в-начало"#Лемма2|леммы об изменении допустимой цепи (для операции push)]] следует, что операции проталкивания не создают допустимых ребер. То есть любое допустимое ребро могло быть создано только в результате выполнения операции подъема. Но вершина <tex>u</tex> не подвергается подъему во время прохода, а любая другая вершина <tex>v</tex>, для которой вызывалась операция подъема, во время данного прохода, не имеет после подъема допустимых ребер, что следует из [[Алгоритм "поднять-в-начало"#Лемма3|леммы об изменении допустимой цепи (для операции relabel)]]. Значит, в конце прохода все ребра, выходящие из <tex>u</tex>, останутся недопустимыми.
}}
== Алгоритм ==
Инициализируем предпоток и высоты, с помощью операции [[Метод проталкивания предпотока#Схема алгоритма|<tex>\mathtt{initializePreflow}</tex>]], список . Список <tex>L</tex> {{---}} список для хранения всех вершин графа, кроме стока и истока. Проинициализируем указатель <tex>current</tex> каждой вершины <tex>u</tex>, чтобы он указывал на первую вершину в списке <tex>u</tex>.
Пройдем по списку <tex>L</tex> , разгружая вершины, начиная с первой вершины. И если операция '''<tex>\mathtt{discharge''' }</tex> изменила высоту вершины, то перемещаем ее в начало списка <tex>L</tex>. Передвинем указатель на следующую вершину списке <tex>L</tex>, если . Если после разгрузки была изменена высота, то берем следующую вершину в новом списке <tex>L</tex>.
'''function''' <tex>\mathtt{relabelToFront}(s, t): </tex>''' <tex>\mathtt{initializePreflow(s)}</tex> <tex>L = V / \setminus \{s, t\}</tex> '''for''' u <tex>u \in V </tex> V <tex>\setminus</ tex> <tex>\{s, t\}</tex> <tex>current[u] = head[N[u]]</tex> <tex>u = head[L]</tex> '''while''' <tex>u != \ne null</tex> <tex>oldHeight = h[u]</tex> <tex>\mathtt{discharge(u)}</tex> '''if''' <tex>h[u] > oldHeight</tex> передвинуть <tex>u </tex> в начало списка <tex>L</tex> <tex>u = nextVartexnextVertex[u]</tex>
В приведенном псевдокоде предполагается, что для каждой вершины <tex>u</tex> уже создан список <tex>N[u]</tex>.
<tex>nextVartexnextVertex[u]</tex> возвращает хранит вершину, следующую за <tex>u</tex> в списке <tex>L</tex>. Если <tex>u</tex> {{---}} последняя вершина в списке, то <tex>nextVartexnextVertex[u] = null</tex>.
'''Инвариант цикла''': "при каждом выполнении проверки условия вхождения в цикл '''<tex>\mathtt{while''', список }</tex> вершины в списке <tex>L</tex> является топологическим упорядочением вершин топологически упорядочены в допустимой сети <tex>G_{f, h} = (V, E_{f, h})</tex>, и ни одна вершина, стоящая в списке перед <tex>u</tex>, не имеет избыточного потока".
== Корректность алгоритма ==
Для доказательства корректности алгоритма, то есть чтобы показать что операция <tex>\mathtt{relabelToFront}</tex> вычисляет поток, покажем, что она является реализацией универсального алгоритма [[Метод проталкивания предпотока|проталкивания предпотока]]. Для начала, заметим, что она выполняет операции <tex>push</tex> и <tex>relabel</tex> только тогда, когда они применимы, следует из [[Алгоритм "поднять-в-начало"#Лемма4|лемм о применимости операций push и relabel]]. Покажем, что когда операция <tex>\mathtt{relabelToFront}</tex> завершится, не применима ни одна основная операция. Для этого подробно рассмотрим операцию <tex>\mathtt{relabelToFront}</tex>:
#После вызова <tex>\mathtt{initializePreflow}</tex> <tex>h[s] = |V|</tex> и <tex>h[u] = 0</tex> для всех <tex>u \in V / \setminus {s}</tex>. Так как <tex>|V| \ge geqslant 2</tex>, то ни одно ребро не является допустимым. Значит, <tex>E_{f, h} = \varnothing</tex> и любой порядок множества <tex>V \setminus \{s, t\}</tex> является топологическим упорядочением <tex>G_{f, h}</tex>.#Проверим, что топологическое упорядочение сохранится при проведении итераций цикла '''<tex>\mathtt{while'''}</tex>. Для начала заметим, что сеть может изменится только из-за операций проталкивания и подъема. Из [[Алгоритм "поднять-в-начало"#Лемма2|леммы об изменении допустимой цепи]] нам известно, что после операции проталкивания новые допустимые ребра не появляются, а это значит, что они могли появится только во время выполнения операции подъема. После того как для вершины <tex>u</tex> применили операцию подъема , больше не существует допустимых ребер, входящих в <tex>u</tex>, но могут быть допустимые ребра, выходящие из нее. Таким образом, перемещая <tex>u</tex> в начало списка <tex>L</tex>, все допустимые ребра, выходящие из <tex>u</tex>, удовлетворяют условию топологического упорядочения.
#Проверим, что ни одна вершина, предшествующая <tex>u</tex> в списке <tex>L</tex>, не имеет избытка потока. Пусть вершина <tex>u'</tex> {{---}} вершина <tex>u</tex> на следующей итерации.
##Если <tex>u</tex> подверглась подъему, то вершин предшествующих <tex>u'</tex> на следующей итерации, кроме <tex>u</tex>, нет или если высота <tex>u</tex> не изменилась, то там остались те же вершины, что и ранее. Так как <tex>u</tex> подверглась разгрузке, то она не содержит избытка потока. Значит, если <tex>u</tex> подверглась подъему в процессе разгрузки, то ни одна вершина, предшествующая <tex>u'</tex>, не содержит избытка потока.
##Если высота <tex>u</tex> не поменялась, в процессе во время разгрузки, то вершины, стоящие в списке <tex>L</tex> перед ней, не получили избыток потока, так как <tex>L</tex> топологически упорядочен все время в процессе разгрузки, поэтому каждая операция проталкивания продвигает поток только по вершинам дальше по списку. В этом случае ни одна вершина, предшествующая <tex>u'</tex>, также не имеет избытка потока.
#После завершения цикла <tex>u = null</tex>, поэтому избыток всех вершин равен <tex>0</tex> (инвариант цикла). Значит, ни одна основная операция неприменима.
{{Теорема
|statement =
Время выполнения операции <tex>\mathtt{relabelToFront}</tex> для любой сети <tex>G = (V, E)</tex> составляет <tex>O(V^{3})</tex>.
|proof =
Пусть '''фаза''' {{---}} время между двумя последовательными операциями подъема. Так как всего, по [[Метод проталкивания предпотока#Лемма6|лемме (6)]], выполняется <tex>O(V^{2})</tex> подъемов, значит, в алгоритме всего <tex>O(V^{2})</tex> фаз.
Если операция '''<tex>\mathtt{discharge''' }</tex> не выполняет подъем, то следующий ее вызов происходит дальше по списку <tex>L</tex> <tex>(</tex>длина <tex>L</tex> меньше <tex>|V|)</tex>. Если же подъем выполняется, то следующий вызов происходит уже в другой фазе алгоритма. Значит, каждая фаза содержит не более <tex>|V|</tex> вызовов '''<tex>\mathtt{discharge'''}</tex>.
Таким образом, цикл '''<tex>\mathtt{while''' }</tex> процедуры <tex>\mathtt{relabelToFront}</tex> выполняет работу (без учета операций вызываемых в '''<tex>\mathtt{discharge'''}</tex>), за <tex>O(V^{3})</tex>.
Оценим работу выполнения внутри операции '''<tex>\mathtt{discharge'''}</tex>:
#Обновление указателя <tex>current[u]</tex> выполняется <tex>O(\deg(u))</tex> в том случае, когда вершина <tex>u</tex> подвергается подъему. Значит, для всех вершин время составляет <tex>O(V \deg(u))</tex>. Следовательно, согласно [[Лемма о рукопожатиях|лемме о рукопожатиях]], время равно <tex>O(VE)</tex>.#Пусть <tex>u</tex> {{---}} произвольная вершина сети. Она может быть поднята не более <tex>O(V)</tex> раз, время каждого подъема <tex>O(\deg(u))</tex>. Значит, время всех подъемов ограничивается <tex>O(VE)</tex>.#Из [[Метод проталкивания предпотока#Лемма8|леммы (8)]] следует, что количество насыщающих проталкиваний составляет <tex>O(VE)</tex>. Ненасыщающее проталкивание уменьшает избыток до <tex>0</tex>, после чего разрядка разгрузка останавливается. Следовательно, ненасыщающих проталкиваний не больше, чем вызовов '''<tex>\mathtt{discharge'''}</tex>, то есть <tex>O(V^{3})</tex>.
Таким образом, время выполнения операции <tex>\mathtt{relabelToFront}</tex> составляет <tex>O(V^{3} + VE)</tex>, что эквивалентно <tex>O(V^{3})</tex>.
}}
==См. также==* [[Метод проталкивания предпотока]]* [[Теорема Форда-Фалкерсона]]* [[Алгоритм Форда-Фалкерсона, реализация с помощью поиска в глубину]]* [[Схема алгоритма Диница]]* [[Алоритм Эдмондса-Карпа]] == Источники информации ==
* Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2011. — С. 774—785.
* [http://ru.wikipedia.org/wiki/Алгоритм_проталкивания_предпотока Алгоритм проталкивания предпотока — Википедия]
*[http://e-maxx.ru/algo/preflow_push_faster MAXimal::algo::Модификация метода Проталкивания предпотока для нахождения максимального потока за O (N3)]
[[Категория: Алгоритмы и структуры данных]]
[[Категория: Задача о максимальном потоке]]
Анонимный участник

Навигация