Алгоритм Куна для поиска максимального паросочетания — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
м
Строка 9: Строка 9:
 
: Допустим в паросочетание внесли изменения вдоль дополняющей цепи <tex>(y \rightsquigarrow z)</tex> и из вершины <tex>x</tex> появилась дополняющая цепь.
 
: Допустим в паросочетание внесли изменения вдоль дополняющей цепи <tex>(y \rightsquigarrow z)</tex> и из вершины <tex>x</tex> появилась дополняющая цепь.
 
: Заметим, что эта дополняющая цепь должна вершинно пересекаться с той цепью, вдоль которой вносились изменения, иначе такая же дополняющая цепь из <tex>x</tex> существовала и в исходном паросочетании.<br><br>
 
: Заметим, что эта дополняющая цепь должна вершинно пересекаться с той цепью, вдоль которой вносились изменения, иначе такая же дополняющая цепь из <tex>x</tex> существовала и в исходном паросочетании.<br><br>
: Пусть <tex>p</tex> - ближайшая к <tex>x</tex> вершина, которая принадлежит и новой дополняющей цепи и цепи <tex>(y \rightsquigarrow z)</tex>.
+
: Пусть <tex>p</tex> &#8211; ближайшая к <tex>x</tex> вершина, которая принадлежит и новой дополняющей цепи и цепи <tex>(y \rightsquigarrow z)</tex>.
 
: Тогда <tex>MP</tex> - последнее ребро на отрезке <tex>(y \rightsquigarrow p)</tex> цепи <tex>(y \rightsquigarrow z)</tex>, <tex>NP</tex> - последнее ребро на отрезке <tex>(z \rightsquigarrow p)</tex> цепи <tex>(y \rightsquigarrow z)</tex>, <tex>QP</tex> - последнее ребро лежащее на отрезке <tex>(x \rightsquigarrow p)</tex> новой дополняющей цепи(см. Рисунок 1).<br><br>
 
: Тогда <tex>MP</tex> - последнее ребро на отрезке <tex>(y \rightsquigarrow p)</tex> цепи <tex>(y \rightsquigarrow z)</tex>, <tex>NP</tex> - последнее ребро на отрезке <tex>(z \rightsquigarrow p)</tex> цепи <tex>(y \rightsquigarrow z)</tex>, <tex>QP</tex> - последнее ребро лежащее на отрезке <tex>(x \rightsquigarrow p)</tex> новой дополняющей цепи(см. Рисунок 1).<br><br>
 
: Допустим <tex>MP</tex> принадлежит паросочетанию <tex>M'</tex>, тогда <tex>NP</tex> ему не принадлежит.<br>
 
: Допустим <tex>MP</tex> принадлежит паросочетанию <tex>M'</tex>, тогда <tex>NP</tex> ему не принадлежит.<br>
Строка 21: Строка 21:
 
:Алгоритм просматривает все вершины графа по очереди, запуская из каждой обход (в глубину или в ширину), пытающийся найти увеличивающую цепь, начинающуюся в этой вершине.
 
:Алгоритм просматривает все вершины графа по очереди, запуская из каждой обход (в глубину или в ширину), пытающийся найти увеличивающую цепь, начинающуюся в этой вершине.
  
:'''Краткое описание алгоритма:'''
 
:* Возьмём пустое паросочетание;
 
:* Разобьем граф на две доли;
 
:* Проходя по всем вершинам первой доли пытаемся найти увеличивающую цепь с началом в текущей вершине;
 
::* Если удается найти увеличивающую цепь, выполняем чередование, то есть если ребро входило в цепь, то удалим его из паросочетания, а если не входило, то наоборот добавим.
 
:* Найденное паросочетание и является максимальным.
 
  
 
+
:Задан граф <tex>G(V, E)</tex>, где <tex>V = V_1 + V_2</tex> и <tex>V_1 \cap V_2 = \varnothing</tex>.  
:'''Подробное описание алгоритма.'''
+
:Просматриваем все вершины <tex>v</tex> первой доли графа <tex>u \in V_1</tex>:
:Будем считать, что граф уже разбит на две доли.
 
:Просматриваем все вершины <tex>v</tex> первой доли графа <tex>v = 1 ... n_1</tex>:
 
 
:*Если текущая вершина  уже насыщена текущим паросочетанием (т.е. уже выбрано какое-то смежное ей ребро), то эту вершину пропускаем;
 
:*Если текущая вершина  уже насыщена текущим паросочетанием (т.е. уже выбрано какое-то смежное ей ребро), то эту вершину пропускаем;
:*Иначе алгоритм пытается насытить эту вершину, для чего запускается поиск увеличивающей цепи, начинающейся с этой вершины.
+
:*Иначе запускаем поиск увеличивающей цепи, начинающейся с этой вершины.
  
  
 
:Рассмотрим поиск увеличивающей цепи обходом в глубину.
 
:Рассмотрим поиск увеличивающей цепи обходом в глубину.
:* Изначально обход в глубину стоит в текущей ненасыщенной вершине <tex>v</tex> первой доли.
+
:* Запускаем обход от вершины <tex>v</tex>.
 
:* Просматриваем все рёбра из этой вершины, пусть текущее ребро — <tex>(v, to)</tex>.
 
:* Просматриваем все рёбра из этой вершины, пусть текущее ребро — <tex>(v, to)</tex>.
 
:* Если вершина <tex>to</tex> ещё не насыщена паросочетанием, то включаем ребро <tex>(v, to)</tex> в паросочетание и прекращаем поиск из вершины <tex>v</tex>.
 
:* Если вершина <tex>to</tex> ещё не насыщена паросочетанием, то включаем ребро <tex>(v, to)</tex> в паросочетание и прекращаем поиск из вершины <tex>v</tex>.
Строка 46: Строка 38:
 
: Этот обход, запущенный из вершины <tex>v</tex>, либо найдет увеличивающую цепь, и тем самым насытит вершину, либо же такой увеличивающей цепи не найдёт (и, следовательно, эта вершина  уже не сможет стать насыщенной).
 
: Этот обход, запущенный из вершины <tex>v</tex>, либо найдет увеличивающую цепь, и тем самым насытит вершину, либо же такой увеличивающей цепи не найдёт (и, следовательно, эта вершина  уже не сможет стать насыщенной).
  
: После того, как все вершины <tex>v = 1 ... n_1</tex> будут просмотрены, текущее паросочетание будет максимальным.
 
  
 +
: После того, как все вершины <tex>u \in V_1</tex> будут просмотрены, текущее паросочетание будет максимальным.
 
: Корректность алгоритма следует из [[Теорема о максимальном паросочетании и дополняющих цепях|теоремы Бержа]] и теоремы, описанной выше.<br>
 
: Корректность алгоритма следует из [[Теорема о максимальном паросочетании и дополняющих цепях|теоремы Бержа]] и теоремы, описанной выше.<br>
  

Версия 13:15, 27 февраля 2012

Теорема

Теорема:
Если из вершины [math]x[/math] не существует дополняющей цепи относительно паросочетания [math]M[/math] и паросочетание [math]M'[/math] получается из [math]M[/math] изменением вдоль дополняющей цепи, тогда из [math]x[/math] не существует дополняющей цепи в [math]M'[/math].
Доказательство:
[math]\triangleright[/math]
Рисунок 1.
Рисунок 2.
Пунктиром обозначен путь между двумя вершинами. Ребро красного цвета лежит в паросочетании, а черного - нет.
Доказательство от противного.

Допустим в паросочетание внесли изменения вдоль дополняющей цепи [math](y \rightsquigarrow z)[/math] и из вершины [math]x[/math] появилась дополняющая цепь.
Заметим, что эта дополняющая цепь должна вершинно пересекаться с той цепью, вдоль которой вносились изменения, иначе такая же дополняющая цепь из [math]x[/math] существовала и в исходном паросочетании.

Пусть [math]p[/math] – ближайшая к [math]x[/math] вершина, которая принадлежит и новой дополняющей цепи и цепи [math](y \rightsquigarrow z)[/math].
Тогда [math]MP[/math] - последнее ребро на отрезке [math](y \rightsquigarrow p)[/math] цепи [math](y \rightsquigarrow z)[/math], [math]NP[/math] - последнее ребро на отрезке [math](z \rightsquigarrow p)[/math] цепи [math](y \rightsquigarrow z)[/math], [math]QP[/math] - последнее ребро лежащее на отрезке [math](x \rightsquigarrow p)[/math] новой дополняющей цепи(см. Рисунок 1).

Допустим [math]MP[/math] принадлежит паросочетанию [math]M'[/math], тогда [math]NP[/math] ему не принадлежит.
(Случай, когда [math]NP[/math] принадлежит паросочетанию [math]M'[/math] полностью симметричен.)

Поскольку паросочетание [math]M'[/math] получается из [math]M[/math] изменением вдоль дополняющей цепи [math](y \rightsquigarrow z)[/math], в паросочетание [math]M[/math] входило ребро [math]NP[/math], а ребро [math]MP[/math] нет.
Кроме того, ребро [math]QP[/math] не лежит ни в исходном паросочетании [math]M[/math], ни в паросочетании [math]M'[/math], в противном случае оказалось бы, что вершина [math]p[/math] инцидентна нескольким ребрам из паросочетания, что противоречит определению паросочетания.

Тогда заметим, что цепь [math](x \rightsquigarrow z)[/math], полученная объединением цепей [math](x \rightsquigarrow p)[/math] и [math](p \rightsquigarrow z)[/math], по определению будет дополняющей в паросочетании [math]M[/math], что приводит к противоречию, поскольку в паросочетании [math]M[/math] из вершины [math]x[/math] не существует дополняющей цепи.
[math]\triangleleft[/math]

Алгоритм

Алгоритм просматривает все вершины графа по очереди, запуская из каждой обход (в глубину или в ширину), пытающийся найти увеличивающую цепь, начинающуюся в этой вершине.


Задан граф [math]G(V, E)[/math], где [math]V = V_1 + V_2[/math] и [math]V_1 \cap V_2 = \varnothing[/math].
Просматриваем все вершины [math]v[/math] первой доли графа [math]u \in V_1[/math]:
  • Если текущая вершина уже насыщена текущим паросочетанием (т.е. уже выбрано какое-то смежное ей ребро), то эту вершину пропускаем;
  • Иначе запускаем поиск увеличивающей цепи, начинающейся с этой вершины.


Рассмотрим поиск увеличивающей цепи обходом в глубину.
  • Запускаем обход от вершины [math]v[/math].
  • Просматриваем все рёбра из этой вершины, пусть текущее ребро — [math](v, to)[/math].
  • Если вершина [math]to[/math] ещё не насыщена паросочетанием, то включаем ребро [math](v, to)[/math] в паросочетание и прекращаем поиск из вершины [math]v[/math].
  • Иначе, если вершина [math]to[/math] уже насыщена каким-то ребром [math](p, to)[/math] и не посещена, то просто перейдем в нашем обходе в вершину [math]p[/math].
    • Пробуем найти часть увеличивающей цепи из вершины [math]p[/math].
    • Если получилось, то удаляем из паросочетания ребро [math](p, to)[/math], а вместо него добавляем [math](v, to)[/math]
Этот обход, запущенный из вершины [math]v[/math], либо найдет увеличивающую цепь, и тем самым насытит вершину, либо же такой увеличивающей цепи не найдёт (и, следовательно, эта вершина уже не сможет стать насыщенной).


После того, как все вершины [math]u \in V_1[/math] будут просмотрены, текущее паросочетание будет максимальным.
Корректность алгоритма следует из теоремы Бержа и теоремы, описанной выше.

Релизация

Граф [math]G[/math] хранится списками смежности [math]g[v][i][/math]
Функция [math]dfs(v)[/math] — обход в глубину, возвращает [math]true[/math] если есть увеличивающая цепь из вершины [math]v[/math].
В массиве [math]mt[/math] хранятся паросочетания. Паросочетание есть ребро [math](i, mt[i])[/math].


bool dfs(int v) 
{
    if (used[v])
        return false;

    used[v] = true;
    for (int i = 0; i < g[v].size(); i++)
    {
        int to = g[v][i];
        if (mt[to] == -1 || dfs(mt[to]))
        {
            mt[to] = v;
            return true;
        }
    }
    return false;
}

int main()
{
    ... чтение графа ...
    mt.assign (k, -1);
    for (int v = 0; v < n; v++)
    {
        used.assign(n, false);
        dfs(v); 
    }

    for (int i = 0; i < k; i++)
        if (mt[i] != -1)
            ... вывод ...

}

Время работы

Итак, алгоритм Куна можно представить как серию из [math]n_1[/math] запусков обхода в глубину на всём графе.
Следовательно, всего этот алгоритм исполняется за время [math]O(nm)[/math], где [math]m[/math] — количество ребер, что в худшем случае есть [math]O(n^3)[/math].


Более точная оценка:
В описанной выше реализации запуски обхода в глубину/ширину происходят только из вершин первой доли, поэтому весь алгоритм исполняется за время [math]O(n_1m)[/math] , где [math]n_1[/math] — число вершин первой доли. В худшем случае это составляет [math]O(n_1^2n_2)[/math], где [math]n_2[/math] — число вершин второй доли.

Ссылки

Источники

MAXimal :: algo :: Алгоритм Куна нахождения наибольшего паросочетания
Асанов М., Баранский В., Расин В. — Дискретная математика: Графы, матроиды, алгоритмы — СПб.: Издательство "Лань", 2010. — 291 стр.