Алгоритм Куна: как найти максимальное паросочетание

Алгоритм Куна - простой и наглядный способ найти максимальное паросочетание в двудольном графе за . Назван в честь Гарольда Куна, который в 1955 году описал его идею в той же статье, где предложил венгерский алгоритм для взвешенной задачи о назначении. В невзвешенной постановке достаточно куновского ядра: повторяющийся поиск увеличивающих цепей (augmenting paths) обычным DFS из каждой непокрытой вершины. Алгоритм решает классическую задачу - распределить работников по работам, студентов по проектам, поезда по платформам - когда у каждой пары «можно/нельзя» и хочется покрыть как можно больше пар одновременно.
Что такое двудольное паросочетание
Граф двудольный, если его вершины разбиты на две доли и так, что любое ребро соединяет вершину из с вершиной из . Внутри одной доли рёбер нет. Это естественная модель для отношений «между двумя множествами»: работники–работы, абитуриенты–вузы, заявки–слоты.
Паросочетание - это набор рёбер без общей вершины. Каждая вершина либо покрыта ровно одним ребром из , либо свободна. Максимальное паросочетание - наибольшее по размеру среди всех возможных. Совершенное (perfect) - то, в котором покрыты вообще все вершины; существует не всегда. Кун находит максимальное; будет ли оно совершенным - видно по сравнению размера ответа с .
Augmenting path - главная идея
Ключевое понятие - увеличивающая (alternating) цепь. Это путь в графе, который:
- Начинается в непокрытой вершине левой доли .
- Чередует рёбра: сначала рёбро не из , затем из , затем не из , и так далее.
- Заканчивается в непокрытой вершине правой доли .
По такой цепи легко увеличить паросочетание на 1: «инвертировать» её - рёбра из удалить, рёбра не из добавить. Внутренние вершины остаются покрыты ровно одним ребром, а свободные концы и становятся покрытыми. Размер растёт на единицу.
Теорема Бержа (1957) - фундамент алгоритма: паросочетание максимально тогда и только тогда, когда в графе нет увеличивающих цепей относительно . Отсюда прямой план: стартовать с пустого , искать увеличивающие цепи и инвертировать их, пока цепи находятся.
Псевдокод
В двудольном графе поиск увеличивающей цепи устроен особенно просто: достаточно DFS из непокрытой вершины . Алгоритм поддерживает массив - какая вершина из сейчас спарена с (или , если свободна).
function try_kuhn(u):
для каждой v ∈ adj[u]:
если used[v]: continue
used[v] = true
если match[v] == -1 или try_kuhn(match[v]):
match[v] = u
return true
return false
function kuhn(L, R, adj):
match[v] = -1 для всех v ∈ R
answer = 0
для каждой u ∈ L:
used[v] = false для всех v ∈ R
если try_kuhn(u):
answer += 1
return answer
Что делает рекурсивный вызов try_kuhn(u). Из перебираем соседей в правой доле. Если свободна - спарили с и вернули true. Если занята какой-то - пытаемся «перевесить» на другую вершину рекурсивным try_kuhn(u'). Если получилось - освободившаяся достаётся . Если не получилось - пробуем следующего соседа .
DFS из - это и есть поиск увеличивающей цепи, начинающейся в . Массив used[] защищает от повторного посещения вершины в одном DFS и обнуляется перед каждой попыткой расширить паросочетание.
Сложность
Внешний цикл по запускает DFS не более раз. Каждый DFS просматривает каждое ребро не более одного раза (за счёт used), то есть его стоимость . Итого:
Эта оценка груба, но без специальных ухищрений плохо улучшаема в общем случае. На практике алгоритм Куна часто работает заметно быстрее - особенно если паросочетание небольшое: тогда DFS быстро завершается, не успев пройти все рёбра.
Память - на список смежности плюс массивы и .
Подробный пример
Пять работников и пять задач . Рёбра - «работник умеет выполнить задачу»:
A - 1, 2 B - 1, 3 C - 2, 3 D - 4 E - 3, 4, 5
Шаг 1. DFS из : свободна → . Шаг 2. Из : занята , но умеет ещё - перевешиваем. Цепь . . Шаг 3. Из : занята , перевес не выходит. свободна → . Шаг 4. Из : свободна → . Шаг 5. Из : и заняты, цепи перевеса не находятся, но свободна → .
Итог: совершенное паросочетание . Без ребра алгоритм остановился бы на размере 4, и теорема Бержа гарантировала бы максимальность.
Связь с теоремой Кёнига
Минимальное вершинное покрытие - набор вершин такой, что каждое ребро инцидентно хотя бы одной вершине из .
Теорема Кёнига (1931): в двудольном графе
Это редкий случай, когда задача о покрытии (NP-трудная в общих графах) становится полиномиальной: прогон Куна даёт и паросочетание, и явное минимальное покрытие. Конструктивно: пусть - непокрытые вершины . Из обходим по нерёбрам вправо, из - по рёбрам влево, помечаем посещённые. Тогда .
Двойственно - теорема Холла: совершенное паросочетание из в существует тогда и только тогда, когда для любого выполнено .
Применения
- Задача о назначении. Распределить работников по работам так, чтобы каждый получил подходящую задачу. Если важны только «может/не может» - алгоритм Куна. Если у каждой пары есть числовая стоимость и нужно минимизировать суммарную - нужен венгерский алгоритм (), который в этой же статье Куна и был предложен.
- Расписание. Аудитории на лекции, поезда на платформы, врачи на смены. Двудольная модель: одна доля - слоты, другая - события; ребро - «событие можно поставить в этот слот».
- Распределение ресурсов. Серверы на запросы, лицензии на пользователей, грузовики на маршруты.
- Доказательства существования. Многие комбинаторные утверждения - теорема Дилворта, формулы для перманентов 0/1-матриц, разложения латинских квадратов - сводятся к существованию совершенного паросочетания и поэтому проверяются Куном.
Если по условию у вас числа на рёбрах и просят минимизировать/максимизировать сумму - это не Кун, а венгерский алгоритм. Кун решает «да/нет»-задачу, его выход - число рёбер в максимальном паросочетании, а не их суммарный вес.
Сравнение с венгерским алгоритмом и Хопкрофтом-Карпом
- Кун - максимальное паросочетание в невзвешенном двудольном графе, . Простой код в 20 строк.
- Венгерский алгоритм (Kuhn, 1955; Munkres, 1957) - минимальное по сумме весов совершенное паросочетание во взвешенном двудольном графе, . Использует двойственные переменные и «эквивалентную матрицу».
- Хопкрофт-Карп (1973) - то же, что Кун, но за . Вместо одной увеличивающей цепи за раз BFS-уровнями находит множество вершинно-непересекающихся кратчайших цепей и инвертирует их разом. На разреженных графах против .
Для учебных задач и графов до пары тысяч вершин Кун достаточен. Для серьёзных размеров - Хопкрофт-Карп. Для взвешенной задачи о назначении - венгерский, и никакой Кун его не заменит.
Частые ошибки
- Применяют Куна к не-двудольному графу. В общем графе понятие увеличивающей цепи сложнее (blossom-стяжки, алгоритм Эдмондса 1965 года), и обычный DFS работает неверно. Кун - только для двудольных.
- Забывают обнулять
usedперед каждым внешним вызовом.usedзащищает от циклов внутри одного DFS; между DFS-ами его обязательно сбрасывать, иначе помеченные прошлой попыткой вершины остаются недоступны. - Считают, что Кун даёт минимум по весу. Он не учитывает веса вообще. Если они есть и важны - это венгерский, не Кун.
- Путают «максимальное» и «совершенное» паросочетание. Кун возвращает максимальное; совершенно оно или нет - зависит от графа. После прогона сравните размер с или .
FAQ
Чем алгоритм Куна отличается от венгерского? Кун решает невзвешенную задачу - найти как можно больше совместимых пар. , код в 20 строк. Венгерский алгоритм минимизирует сумму стоимостей при покрытии каждой вершины, . Куна нельзя «приспособить» к весам - нужен венгерский.
Что такое увеличивающая цепь простыми словами? Путь, который начинается и заканчивается в свободных вершинах разных долей и чередует рёбра «вне / внутри ». Если поменять «вне» и «внутри» местами, паросочетание вырастет на единицу. Кун ищет такие цепи, пока находит.
Когда применять Хопкрофта-Карпа вместо Куна? Когда и велики (десятки тысяч и больше). Хопкрофт-Карп - , на разреженных графах против у Куна. На маленьких задачах Кун обычно быстрее из-за меньшей константы.
Коротко
Алгоритм Куна находит максимальное паросочетание в двудольном графе за . Идея - теорема Бержа: пока существует увеличивающая чередующаяся цепь из свободной вершины в свободную вершину , паросочетание можно увеличить инверсией её рёбер. Кун ищет такие цепи обычным DFS из каждой непокрытой вершины слева, поддерживая массив match пар правой доли. Через теорему Кёнига алгоритм заодно даёт минимальное вершинное покрытие. Для взвешенной задачи о назначении нужен венгерский алгоритм (), для больших невзвешенных графов - Хопкрофт-Карп (). Кун - простой, наглядный и достаточный в большинстве учебных и средних задач.
Читайте также

Алгоритм Прима - как построить остовное дерево по шагам
Разбираем, как алгоритм Прима шаг за шагом строит минимальное остовное дерево графа: идея жадного выбора, лемма о разрезе и трассировка на конкретном примере.

Алгоритм Эдмондса-Карпа: поиск максимального потока
Алгоритм Эдмондса-Карпа находит максимальный поток в сети: BFS ищет дополняющий путь, что даёт полиномиальную сложность. Разбираем идею, реализацию и оценку.

Алгоритм Дейкстры: как найти кратчайший путь в графе
Разбираем алгоритм Дейкстры на взвешенном графе по шагам: как он находит кратчайшие пути от стартовой вершины и почему ломается на ребрах с отрицательными весами.