Задача о числе путей в ациклическом графе
Задача о числе путей в ациклическом графе - одна из классических задач на тему динамического программирования. В этой задаче нам дан ациклический граф
и две вершины и . Необходимо посчитать количество путей из вершины в вершину по рёбрам графа .Число таких путей может быть велико даже на небольших графах, поэтому перебор всех возможных вариантов займет много времени. Однако, данную задачу можно решить гораздо быстрее с помощью динамики.
Содержание
Решение задачи
Перебор всех возможных путей
Небольшая модификация алгоритма обхода в глубину. Запустим обход в глубину от вершины . При каждом посещении вершины проверим, не является ли она искомой вершиной . Если это так, то ответ увеличивается на единицу и обход прекращается. В противном случае производится запуск обхода в глубину от всех вершин, ребра в которые выходят из , причем он производится независимо от того, были ли эти вершины посещены ранее, или нет.
Функция
принимает начальную вершину и конечную вершину . В глобальной переменной содержится ответ.
answer = 0 count(v) if v == t answer += 1 else for(всехсмежных с ) count(to) countPaths(s, t) answer = 0 count(s) return answer
Время работы данного алгоритма в худшем случае
, где - количество путей в графе.Метод динамического программирования
Пусть
- количество путей до вершины . Можно заметить, что зависит только от вершин, ребра из которых входят в . Тогда таких , что ребро из в . Мы свели нашу задачу к более мелким подзадачам, причем мы также знаем, что . Это позволяет решить задачу методом динамического программирования.Псевдокод
Пусть
- стартовая вершина, а - конечная, для нее и посчитаем ответ. Будем поддерживать массив , где - количество путей до вершины и массив , где , если ответ для вершины уже посчитан, и в противном случае. Изначально для всех вершин , кроме , а . Функция будет возвращать ответ для вершины . Удобнее всего это реализовать с помощью ленивой рекурсии, тогда значения массива будут вычисляться по мере необходимости, а засчет запоминания результатов они не будут считаться лишний раз:
count(v) if w[v] return d[v] else sum = 0 for(всехсмежных с ): sum += count(c) d[v] = sum w[v] = true return sum countPaths(s, t): d[s] = 1 w[s] = true answer = count(t) return answer
Значение функции
считается для каждой вершины один раз, а внутри нее рассматриваются все такие ребра . Всего таких ребер для всех вершин в графе , следовательно, время работы алгоритма в худшем случае оценивается как , где - количество вершин графа, - количество ребер.Пример работы
Рассмотрим пример работы алгоритма на следующем графе: