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

Материал из Викиконспекты
Версия от 11:36, 30 декабря 2012; Whiplash (обсуждение | вклад) (Схема алгоритма)
Перейти к: навигация, поиск

Алгоритм "поднять-в-начало" (relabel-to-front) основан на методе проталкивание предпотока, но из-за тщательного выбора порядка выполнения операций проталкивания и подъема, время выполнения данного алгоритма составляет [math]O(V^{3})[/math], что асимптотически не хуже, чем [math]O(V^{2}E)[/math].

Допустимые ребра

[math]G = (V, E)[/math]сеть с истоком [math]s[/math] и стоком [math]t[/math], [math]f[/math]предпоток в [math]G[/math], [math]h[/math]функция высоты.

Определение:
Допустимое ребро (admissible edge) — ребро [math]uv[/math], у которого [math]c_{f}(u, v) \gt 0[/math] и [math]h(u) = h(v) + 1[/math]. В противном случае [math]uv[/math] называется недопустимым (inadmissible).


Определение:
Допустимая сеть (admissible network) — сеть [math]G_{f, h} = (V, E_{f, h})[/math], где [math]E_{f, h}[/math] — множество допустимых ребер.


Лемма (Допустимая сеть является ациклической):
Допустимая сеть [math]G_{f, h} = (V, E_{f, h})[/math] является ациклической.
Доказательство:
[math]\triangleright[/math]

Пусть в [math]G_{f, h}[/math] существует циклический путь [math]p = \left \langle v_0, v_1, \dots, v_k \right \rangle[/math], где [math]k \gt 0[/math].

[math] ~ ~ h(v_{i - 1}) = h(v_{i}) + 1[/math] для [math]i = 1, 2, \dots, k[/math], так как каждое ребро данного пути допустимое. Просуммировав равенства вдоль циклического пути, получаем:

[math]\sum \limits_{i = 1}^{k} h(v_{i - 1}) = \sum \limits_{i = 1}^{k} (h(v_{i}) + 1) = \sum \limits_{i = 1}^{k} h(v_{i}) + k[/math]

Так как каждая вершина циклического пути [math]p[/math] встречается при суммировании по одному разу это значит то, что [math]k = 0[/math], что противоречит первоначальному предположению. Значит, допустимая сеть является ациклической.
[math]\triangleleft[/math]


Лемма (Об изменении допустимой цепи, с помощью операции проталкивания):
Если вершина [math]u[/math] переполнена и ребро [math](u, v)[/math] допустимое, то применяемая операция [math]push(u, v)[/math] не создает новые допустимые ребра, но может привести к тому, что ребро [math](u, v)[/math] станет недопустимым.
Доказательство:
[math]\triangleright[/math]

Так как ребро [math](u, v)[/math] допустимое то, по определению допустимого ребра, из [math]u[/math] в [math]v[/math] можно протолкнуть поток. Из-за того что [math]u[/math] — переполнена, вызываем операцию [math]push(u, v)[/math]. В результате выполнения операции может быть создано остаточное ребро [math](u, v)[/math]. Так ребро [math](u, v)[/math] допустимое то, [math]h[v] = h[u] - 1[/math], а это значит, что ребро [math](v, u)[/math] не может стать допустимым.

Если выполненная операция [math]push(u, v)[/math] является насыщающим проталкиванием, то после ее выполнения [math]c_{f}(u, v) = 0[/math] и ребро [math](u, v)[/math] становится недопустимым.
[math]\triangleleft[/math]


Лемма (Об изменении допустимой цепи, с помощью операции подъема):
Если вершина [math]u[/math] переполнена и не имеется допустимых ребер, выходящих из [math]u[/math], то применяется операция [math]relabel(u)[/math]. После подъема появляется по крайней мере одно допустимое ребро, выходящее из [math]u[/math], но нет допустимых ребер, входящих в [math]u[/math].
Доказательство:
[math]\triangleright[/math]

Рассмотрим вершину [math]u[/math]. Если [math]u[/math] переполнена, то, согласно лемме (2), к ней может быть применима либо операция проталкивания, либо операция подъема. А так как не существует допустимых ребер для [math]u[/math], то протолкнуть поток не возможно, значит, применяется операция [math]relabel(u)[/math]. После данного подъема [math]h[u] = 1 + min \{ h[v]: (u, v) \in E_{f} \}[/math]. Значит, если [math]u[/math] — вершина указанного множества, в которой реализуется минимум, то [math](u, v)[/math] становится допустимым. А это значит, что после подъема существует хотя бы одно допустимое ребро, выходящее из [math]u[/math].

Пусть существует такая вершина [math]u[/math], после подъема, что ребро [math](u, v)[/math] допустимо. Тогда [math]h[v] = h[u] + 1[/math], значит, перед подъемом [math]h[v] \gt h[u] + 1[/math]. Но между вершинами, высоты которых отличаются более чем на 1, не существует остаточных сетей. Кроме того, подъем вершины не меняет остаточную сеть. Значит, ребро [math](v, u)[/math] не может находится в допустимой сети, так как оно не принадлежит остаточной сети.
[math]\triangleleft[/math]

Идея

Операция разгрузки (discharge)

Разгрузка (discharge) — операция, которая применяется к переполненной вершине [math]u[/math], для того чтобы протолкнуть поток через допустимые ребра в смежные вершины, при необходимости поднимая [math]u[/math], делая недопустимые ребра, выходящие из вершины [math]u[/math], допустимыми.

Будем хранить для каждой вершины [math]u[/math] список [math]N[u][/math] (список вершин смежных с ней). То есть список [math]N[u][/math] содержит каждую вершину [math]v[/math] такую, что в сети [math]G = (V, E) ~ (u, v) \in E[/math] или [math](v, u) \in E[/math].

На первую вершину в списке указывает указатель [math]head[N[u]][/math]. Для перехода к следующей вершине в списке за [math]w[/math], поддерживается указатель [math]next[w][/math]. Он равен [math]null[/math], если [math]w[/math] — последняя вершина в списке.

Для каждой вершины [math]u[/math] указатель [math]current[u][/math] — указатель на текущую вершину списка. Изначально [math]current[u] = head[N[u]][/math].

discharge(u)
    while e[u] > 0
        v = current[u]
        if v = null
            relabel(u)
            current[u] = head[N[u]]
        else
            if c(u, v) - f(u, v) > 0 and h[u] = h[v] + 1
                push(u, v)
            else
                current[u] = next[v]

Операция завершится только тогда, когда избыток [math]e(u)[/math] станет равным нулю, и ни подъем, ни перемещение указателя [math]current[u][/math] не влияет на значение [math]e(u)[/math].

Докажем то, что когда операция discharge вызывает операции push и relable, эти операции применимы.

Лемма:
Когда операция [math]discharge[/math] вызывает в операцию [math]push(u, v)[/math], то для пары вершин [math](u, v)[/math] применима операция проталкивания.
Доказательство:
[math]\triangleright[/math]
Проверки операции [math]discharge[/math], сделанные до вызова операции проталкивания, гарантируют то, что операция [math]push[/math] будет вызвана только тогда, когда она применима. То есть [math]e(u) \gt 0[/math], [math]c_{f}(u, v) \gt 0[/math] и [math]h(u) = h(v) + 1[/math].
[math]\triangleleft[/math]
Лемма:
Когда операция [math]discharge[/math] вызывает в операцию [math]relabel(u)[/math], то для вершины [math]u[/math] применим подъем.
Доказательство:
[math]\triangleright[/math]

Из леммы об изменении допустимой цепи (для операции relabel) и условия [math]e(u) \gt 0[/math] следует, что для доказательства данной леммы необходимо показать, что все ребра выходящие из [math]u[/math], являются недопустимыми.

Каждый проход операции [math]discharge(u)[/math] начинается с головы списка [math]N[u][/math] и оканчивается, когда [math]current[u] = null[/math]. Именно тогда вызывается [math]relabel(u)[/math] и начинается новый проход. К концу прохода все ребра, выходящие из [math]u[/math], станут недопустимыми. Так как из леммы об изменении допустимой цепи (для операции push) следует, что операции проталкивания не создают допустимых ребер. То есть любое допустимое ребро могло быть создано только в результате выполнения операции подъема. Но вершина [math]u[/math] не подвергается подъему во время прохода, а любая другая вершина [math]v[/math], для которой вызывалась операция подъема, во время данного прохода, не имеет после подъема допустимых ребер, что следует из леммы об изменении допустимой цепи (для операции relabel). Значит, в конце прохода все ребра, выходящие из [math]u[/math], останутся недопустимыми.
[math]\triangleleft[/math]

Алгоритм

Для данного алгоритма будем использовать связанный список [math]L[/math], для хранения всех вершин, кроме стока и истока. Вершины в списке топологически отсортированы в соответствии с допустимой сетью.

initializePreflow - операция для создания начального предпотока в сети.

 relabelToFront(s, t)
     initializePreflow(s)
     L = V / {s, t}
     for u [math]\in[/math] V / {s, t}
         current[u] = head[N[u]]
     u = head[L]
     while u != null
         oldHeight = h[u]
         discharge(u)
         if h[u] > oldHeight
             передвинуть u в начало списка L
         u = nextVartex[u]

В приведенном псевдокоде предполагается, что для каждой вершины [math]u[/math] уже создан список [math]N[u][/math]. nextVartex[u] возвращает вершину, следующую за [math]u[/math] в списке [math]L[/math]. Если [math]u[/math] - последняя вершина в списке, то [math]nextVartex[u] = null[/math].

Анализ

Источники