palliative's blog

By palliative, 4 years ago, In Russian

Мне показалось забавным попытаться соединить два популярных алгоритма в один

Рассмотрим следующую задачу.
Дан связный неорграф из $$$n$$$ вершин без петель и кратных рёбер. Также задан шаблон обхода DFS — $$$a_1, a_2, \ldots, a_n$$$, который означает, что $$$i$$$-ой по счёту вершиной нужно посетить $$$a_i$$$, если $$$a_i \neq 0$$$, и любую другую иначе.
Требуется найти удовлетворяющий шаблону обход, или сообщить что такого нет.

Пример

Идея решения заключается в том, чтобы на графе всевозможных состояний обхода DFS запустить BFS.
Для одного состояния будем хранить три массива:

  • used — была ли посещена вершина
  • stack — текущий стек вершин
  • path — общий проделанный путь
Класс состояния обхода (C++)

Заведём очередь q (как и полагается в BFS). Так как мы, скорее всего, не знаем с какой вершины нужно начать обход, создадим фиктивную вершину и соединим её со всеми остальными. Изначально в q положим состояние с этой вершиной.

Пока q не пуста, и ещё не нашли ответ, извлекаем оттуда очередное состояние cur. Пусть last — последняя вершина в стеке, то есть та вершина, в которой сейчас остановился cur. Из непосещённых соседей last выбираем те, пройдя в которые заданный порядок входов в вершины не нарушится, и добавляем нужные состояния в очередь. Если же все соседи посещены, то добавляем в очередь состояние cur, но с извлечённой из стека последней вершиной (что соответствует обратному ходу рекурсии в DFS).
Ответом будет состояние с длиной пути $$$n$$$.

Код получился несколько громоздким (большей частью из-за ООП, который был выбран для понятности), но довольно простым.

Решение (C++)

Асимптотика
Сначала найдём $$$V, E$$$ — количество вершин и ребёр у нашего графа состояний.
Для простоты будем считать, что изначально на вход был дан полный граф и шаблон состоит полностью из нулей (то есть нет ограничений на порядок вершин). В таком случае состояние характеризуется двумя числами — длиной пройденного пути и самим путём.
При длине пути $$$0$$$ есть ровно одно состояние (состоящее из фиктивной вершины). При длине $$$= 1$$$ есть $$$n$$$ состояний и т.д. — при длине $$$i \le n$$$ есть $$$(n)_i = \frac{n!}{(n - i)!} = n \cdot (n - 1) \cdot \ldots \cdot (n - i + 1)$$$ состояний.
Тогда всего вершин $$$V = \sum_{i = 0}^n (n)_i$$$.
Из каждого состояния можно перейти в не более чем $$$n$$$ других, поэтому $$$E \le nV$$$.
BFS на графе из $$$V$$$ вершин и $$$E$$$ рёбер работает за $$$O(V + E)$$$. Также учтём, что одно состояние обрабатывается за $$$O(n)$$$ из-за копирования массивов. Итого алгоритм работает за $$$O(n^2V) = O\Big(n^2 \cdot \sum_{i = 0}^n (n)_i\Big) = O\big(n^2(n + 1)!\big)$$$.

Если учитывать ограничения на времена входа в вершины, можно получить асимптотику получше.

Более сложная оценка времени работы

Warning!
Алгоритм не претендует на практическую полезность.
Любое совпадение считать случайным :)

  • Vote: I like it
  • +7
  • Vote: I do not like it