MAXimal | |
добавлено: 13 Aug 2010 19:48 Содержание [скрыть] Алгоритм ДиницаПостановка задачиПусть дана сеть, т.е. ориентированный граф Требуется найти в этой сети поток Немного историиЭтот алгоритм был опубликован советским (израильским) учёным Ефимом Диницем (Yefim Dinic, иногда пишется как "Dinitz") в 1970 г., т.е. даже на два года раньше опубликования алгоритма Эдмондса-Карпа (впрочем, оба алгоритма были независимо открыты в 1968 г.). Кроме того, следует отметить, что некоторые упрощения алгоритма были произведены Шимоном Ивеном (Shimon Even) и его учеником Алоном Итаи (Alon Itai) в 1979 г. Именно благодаря им алгоритм получил свой современный облик: они применили к идее Диница концепцию блокирующих потоков Александра Карзанова (Alexander Karzanov, 1974 г.), а также переформулировали алгоритм к той комбинации обхода в ширину и в глубину, в которой сейчас этот алгоритм и излагается везде. Развитие идей по отношению к потоковым алгоритмам крайне интересно рассматривать, учитывая "железный занавес" тех лет, разделявший СССР и Запад. Видно, как иногда похожие идеи появлялись почти одновременно (как в случае алгоритма Диница и алгоритма Эдмондса-Карпа), правда, имея при этом разную эффективность (алгоритм Диница на один порядок быстрее); иногда же, наоборот, появление идеи по одну сторону "занавеса" опережало аналогичный ход по другую сторону более чем на десятилетие (как алгоритм Карзанова проталкивания в 1974 г. и алгоритм Гольдберга (Goldberg) проталкивания в 1985 г.). Необходимые определенияВведём три необходимых определения (каждое из них является независимым от остальных), которые затем будут использоваться в алгоритме Диница. Остаточной сетью
Стоит отметить, что при таком определении в остаточной сети могут появляться кратные рёбра: если в исходной сети было как ребро Остаточное ребро можно интуитивно понимать как меру того, насколько ещё можно увеличить поток вдоль какого-то ребра. В самом деле, если по ребру Блокирующим потоком в данной сети называется такой поток, что любой путь из истока Блокирующий поток не обязательно максимален. Теорема Форда-Фалкерсона говорит о том, что поток будет максимальным тогда и только тогда, когда в остаточной сети не найдётся Слоистая сеть для данной сети строится следующим образом. Сначала определяются длины кратчайших путей из истока Очевидно, слоистая сеть ациклична. Кроме того, любой Построить слоистую сеть по данной сети очень легко: для этого надо запустить обход в ширину по рёбрам этой сети, посчитав тем самым для каждой вершины величину Примечание. Термин "слоистая сеть" в русскоязычной литературе не употребляется; обычно эта конструкция называется просто "вспомогательным графом". Впрочем, на английском языке обычно используется термин "layered network". АлгоритмСхема алгоритмаАлгоритм представляет собой несколько фаз. На каждой фазе сначала строится остаточная сеть, затем по отношению к ней строится слоистая сеть (обходом в ширину), а в ней ищется произвольный блокирующий поток. Найденный блокирующий поток прибавляется к текущему потоку, и на этом очередная итерация заканчивается. Этот алгоритм схож с алгоритмом Эдмондса-Карпа, но основное отличие можно понимать так: на каждой итерации поток увеличивается не вдоль одного кратчайшего Корректность алгоритмаПокажем, что если алгоритм завершается, то на выходе у него получается поток именно максимальной величины. В самом деле, предположим, что в какой-то момент в слоистой сети, построенной для остаточной сети, не удалось найти блокирующий поток. Это означает, что сток Оценка числа фазПокажем, что алгоритм Диница всегда выполняет менее Лемма 1. Кратчайшее расстояние от истока до каждой вершины не уменьшается с выполнением каждой итерации, т.е. где нижний индекс обозначает номер фазы, перед которой взяты значения этих переменных. Доказательство. Зафиксируем произвольную фазу Заметим, что в остаточную сеть
Лемма 2. Расстояние между истоком и стоком строго увеличивается после каждой фазы алгоритма, т.е.: где штрихом помечено значение, полученное на следующей фазе алгоритма. Доказательство: от противного. Предположим, что после выполнения текущей фазы оказалось, что Эту лемму интуитивно можно понимать следующим образом: на Поскольку длина кратчайшего пути из Поиск блокирующего потокаЧтобы завершить построение алгоритма Диница, надо описать алгоритм нахождения блокирующего потока в слоистой сети — ключевое место алгоритма. Мы рассмотрим три возможных варианта реализации поиска блокирующего потока:
АсимптотикаТаким образом, весь алгоритм Диница выполняется за Единичные сетиЕдиничной сетью ("unit network") называется такая сеть, в которой пропускные способности всех существующих рёбер равны единице, и у любой вершины, кроме истока и стока, либо входящее, либо исходящее ребро единственно. Этот случай является достаточно важным, поскольку в задаче поиска максимального паросочетания построенная сеть является именно единичной. Докажем, что на единичных сетях алгоритм Диница даже в простой реализации (которая на произвольных графах отрабатывает за Во-первых, отметим, что приведённый выше алгоритм поиска блокирующего потока, который на произвольных сетях работает за время Во-вторых, оценим общее количество фаз, которое могло произойти в случае единичных сетей. Пусть уже было произведено С другой стороны, учитывая, что что означает, что ещё через Следовательно, общее число фаз алгоритма Диница, выполняемое на единичных сетях, можно оценить как РеализацияПриведём две реализации алгоритма за Реализация над графами в виде матриц смежностиconst int MAXN = ...; // число вершин const int INF = 1000000000; // константа-бесконечность int n, c[MAXN][MAXN], f[MAXN][MAXN], s, t, d[MAXN], ptr[MAXN], q[MAXN]; bool bfs() { int qh=0, qt=0; q[qt++] = s; memset (d, -1, n * sizeof d[0]); d[s] = 0; while (qh < qt) { int v = q[qh++]; for (int to=0; to<n; ++to) if (d[to] == -1 && f[v][to] < c[v][to]) { q[qt++] = to; d[to] = d[v] + 1; } } return d[t] != -1; } int dfs (int v, int flow) { if (!flow) return 0; if (v == t) return flow; for (int & to=ptr[v]; to<n; ++to) { if (d[to] != d[v] + 1) continue; int pushed = dfs (to, min (flow, c[v][to] - f[v][to])); if (pushed) { f[v][to] += pushed; f[to][v] -= pushed; return pushed; } } return 0; } int dinic() { int flow = 0; for (;;) { if (!bfs()) break; memset (ptr, 0, n * sizeof ptr[0]); while (int pushed = dfs (s, INF)) flow += pushed; } return flow; } Сеть должна быть предварительно считана: должны быть заданы переменные Реализация над графами в виде списков смежностиconst int MAXN = ...; // число вершин const int INF = 1000000000; // константа-бесконечность struct edge { int a, b, cap, flow; }; int n, s, t, d[MAXN], ptr[MAXN], q[MAXN]; vector<edge> e; vector<int> g[MAXN]; void add_edge (int a, int b, int cap) { edge e1 = { a, b, cap, 0 }; edge e2 = { b, a, 0, 0 }; g[a].push_back ((int) e.size()); e.push_back (e1); g[b].push_back ((int) e.size()); e.push_back (e2); } bool bfs() { int qh=0, qt=0; q[qt++] = s; memset (d, -1, n * sizeof d[0]); d[s] = 0; while (qh < qt && d[t] == -1) { int v = q[qh++]; for (size_t i=0; i<g[v].size(); ++i) { int id = g[v][i], to = e[id].b; if (d[to] == -1 && e[id].flow < e[id].cap) { q[qt++] = to; d[to] = d[v] + 1; } } } return d[t] != -1; } int dfs (int v, int flow) { if (!flow) return 0; if (v == t) return flow; for (; ptr[v]<(int)g[v].size(); ++ptr[v]) { int id = g[v][ptr[v]], to = e[id].b; if (d[to] != d[v] + 1) continue; int pushed = dfs (to, min (flow, e[id].cap - e[id].flow)); if (pushed) { e[id].flow += pushed; e[id^1].flow -= pushed; return pushed; } } return 0; } int dinic() { int flow = 0; for (;;) { if (!bfs()) break; memset (ptr, 0, n * sizeof ptr[0]); while (int pushed = dfs (s, INF)) flow += pushed; } return flow; } Сеть должна быть предварительно считана: должны быть заданы переменные
|