Гамильтоновы графы
Версия от 20:32, 9 января 2016; 91.151.202.175 (обсуждение) (→Алгоритм нахождения гамильтового цикла)
Основные определения
Определение: |
Гамильтоновым путём (англ. Hamiltonian path) называется простой путь, приходящий через каждую вершину графа ровно один раз. |
Определение: |
Гамильтоновым циклом (англ. Hamiltonian cycle) называют замкнутый гамильтонов путь. |
Определение: |
Граф называется полугамильтоновым (англ. Semihamiltonian graph), если он содержит гамильтонов путь. |
Определение: |
Граф называется гамильтоновым (англ. Hamiltonian graph), если он содержит гамильтонов цикл. |
Очевидно, что любой гамильтонов граф также и полугамильтонов.
Достаточные условия гамильтоновости графа
Теорема Дирака
Теорема: |
Если и для любой вершины неориентированного графа , то — гамильтонов граф. |
Теорема Оре
Теорема: |
Если и для любых двух различных несмежных вершин и неориентированного графа , то — гамильтонов граф. |
Теорема Поша
Теорема (Поша): |
Пусть граф имеет вершин и выполнены следующие два условия:
|
Теорема Редеи-Камиона
Теорема: |
Любой сильносвязный турнир — гамильтонов. |
Теорема Гуйя-Ури
Теорема (Ghouila-Houri): |
Пусть — сильносвязный ориентированный граф. — гамильтонов. |
Теорема Хватала
Теорема (Хватал): |
Пусть:
Тогда если |
Алгоритм нахождения гамильтового цикла
Зафиксируем начальную вершину
и будем искать гамильтонов цикл наименьшей стоимости — путь от до , проходящий по всем вершинам (кроме первоначальной) один раз. Т.к. искомый цикл проходит через каждую вершину, то выбор не имеет значения. Поэтому будем считать .Подмножества вершин будем кодировать битовыми векторами, обозначим
значение -ого бита в векторе .Обозначим
как наименьшую стоимость пути из вершины в вершину , проходящую (не считая вершины ) единожды по всем тем и только тем вершинам , для которых (т.е. уже найденный оптимальный путь от -ой вершины до -ой, проходящий через те вершины, где . Если ,то эти вершины еще не посещены).- Начальное состояние — когда находимся в 0-й вершине, ни одна вершина не посещена, а пройденный путь равен (т.е. и ).
- Для остальных состояний ( или ) перебираем все возможные переходы в -ую вершину из любой посещенной ранее и выбираем минимальный результат.
- Если возможные переходы отсутствуют, решения для данной подзадачи не существует (обозначим ответ для такой подзадачи как ).
Стоимостью минимального гамильтонова цикла в исходном графе будет значение
— стоимость пути из -й вершины в -ю, при необходимости посетить все вершины. Данное решение требует памяти и времени.Для того, чтобы восстановить сам путь, воспользуемся соотношением
, которое выполняется для всех ребер, входящих в минимальный цикл . Начнем с состояния , , найдем вершину , для которой выполняется указанное соотношение, добавим в ответ, пересчитаем текущее состояние как , . Процесс заканчивается в состоянии , .Прежде чем писать код, скажем пару слов о порядке обхода состояний. Обозначим за
количество единиц в маске (иначе говоря количество пройденных вершин не считая текущей). Тогда, поскольку при рассмотрении состояния мы смотрим на состояния, и , то состояния с большим должны быть посещены позже, чтобы к моменту вычисления текущего состояния были вычислены все те, которые используются для его подсчёта. Однако если использовать рекурсию, об этом можно не беспокоиться (и сэкономить немало кода, времени и памяти).
//Все переменные используются из описания алгоритма, function findCheapest(i, mask): if d[i][mask] != = бесконечность return d[i][mask] for j = 0 .. n - 1 if w(i, j) существует and j-ый бит mask == 1 d[i][mask] = min(d[i][mask], findCheapest(j, mask - 2 ** j) + w(i, j)) return d[i][mask] for i = 0 .. n - 1 for mask = 0 .. 2 ** n - 1 d[i][mask] = d[0][0] = 0; ans = findCheapest(0, 2 ** n - 1) if ans == exit
Дальше ищем сам цикл:
i = 0 mask = 2 ** n - 1 path.push(0) while mask != 0 for j = 0 .. n - 1 if w(i, j) существует and j-ый бит mask == 1 and d[i][mask] == d[j][mask - 2 ** j] + w(i, j) path.push(j) i = j mask = mask - 2 ** j continue
Алгоритм нахождения гамильтового пути
Алгоритм нахождения гамильтонова пути легко получить слегка изменив алгоритм нахождения гамильтонова цикла.
bool findPath(i, mask): if d[i][mask] return true for j = 0 .. n - 1 if w(i, j) существует and j-ый бит mask == 1 if findPath(j, mask - 2 ** j) d[i][mask] = true return d[i][mask] for i = 0 .. n - 1 for mask = 0 .. 2 ** n - 1 d[i][mask] = false d[0][0] = true; ans = findPath(0, 2 ** n - 1) if ans == false exit
Дальше ищем сам путь:
i = 0 mask = 2 ** n - 1 while mask != 0 for j = 0 .. n - 1 if w(i, j) существует and j-ый бит mask == 1 and d[i][mask] == d[j][mask - 2 ** j] == true path.push(j) i = j mask = mask - 2 ** j continue
Источники информации
- Харари Ф. Теория графов: Пер. с англ. / Предисл. В. П. Козырева; Под ред. Г.П.Гаврилова. Изд. 4-е. — М.: Книжный дом "ЛИБРОКОМ", 2009. — 60 с.
- Седжвик Р. Фундаментальные алгоритмы на C++. Алгоритмы на графах. — СПб: ООО «ДиаСофтЮП», 2002.
- Гамильтонов граф