Задача о числе путей в ациклическом графе — различия между версиями
Nafanya (обсуждение | вклад) (→Перебор всех возможных путей) |
Nafanya (обсуждение | вклад) (→Перебор всех возможных путей) |
||
Строка 7: | Строка 7: | ||
Небольшая модификация алгоритма [[Обход в глубину, цвета вершин|обхода в глубину]]. Запустим обход в глубину от вершины <tex>s</tex>. При каждом посещении вершины <tex>v</tex> проверим, не является ли она искомой вершиной <tex>t</tex>. Если это так, то ответ увеличивается на единицу и обход прекращается. В противном случае производится запуск обхода в глубину для всех вершин, в которые есть ребро из <tex>v</tex>, причем он производится независимо от того, были эти вершины посещены ранее, или нет. | Небольшая модификация алгоритма [[Обход в глубину, цвета вершин|обхода в глубину]]. Запустим обход в глубину от вершины <tex>s</tex>. При каждом посещении вершины <tex>v</tex> проверим, не является ли она искомой вершиной <tex>t</tex>. Если это так, то ответ увеличивается на единицу и обход прекращается. В противном случае производится запуск обхода в глубину для всех вершин, в которые есть ребро из <tex>v</tex>, причем он производится независимо от того, были эти вершины посещены ранее, или нет. | ||
− | Функция <tex>countPaths(g, s, t)</tex> принимает граф <tex>g</tex>, начальную вершину <tex>s</tex> и конечную вершину <tex>t</tex>. | + | Функция <tex>countPaths(g, s, t)</tex> принимает граф <tex>g</tex> в виде списка смежности, начальную вершину <tex>s</tex> и конечную вершину <tex>t</tex>. |
'''countPaths'''(g, v, t) | '''countPaths'''(g, v, t) |
Версия 19:48, 3 января 2014
Задача о числе путей в ациклическом графе - одна из классических задач на тему динамического программирования. В этой задаче нам дан ациклический граф
и две вершины и . Необходимо посчитать количество путей из вершины в вершину по рёбрам графа .Содержание
Решение задачи
Перебор всех возможных путей
Небольшая модификация алгоритма обхода в глубину. Запустим обход в глубину от вершины . При каждом посещении вершины проверим, не является ли она искомой вершиной . Если это так, то ответ увеличивается на единицу и обход прекращается. В противном случае производится запуск обхода в глубину для всех вершин, в которые есть ребро из , причем он производится независимо от того, были эти вершины посещены ранее, или нет.
Функция
принимает граф в виде списка смежности, начальную вершину и конечную вершину .countPaths(g, v, t) if v == t return 1 else s = 0 for to in g[v] s += count(g, to, t) return s
Время работы данного алгоритма в худшем случае
, где - количество путей в графе из в . Например, на следующем графе данный алгоритм будет иметь время работы . Если же использовать метод динамичекого программиования, речь о котором пойдет ниже, то асимптотику можно улучшить до .Метод динамического программирования
Пусть
- количество путей от вершины до вершины . Тогда зависит только от вершин, ребра из которых входят в . Тогда таких , что есть ребро из в . Мы свели нашу задачу к меньшим подзадачам, причем мы также знаем, что . Это позволяет решить задачу методом динамического программирования.Псевдокод
Пусть
- стартовая вершина, а - конечная, для нее и посчитаем ответ. Будем поддерживать массив , где - количество путей из вершины до вершины и массив , где , если ответ для вершины уже посчитан, и в противном случае. Изначально для всех вершин , кроме , а . Функция будет возвращать ответ для вершины . Удобнее всего это реализовать в виде рекурсивной функции с запоминанием. В этом случае значения массива будут вычисляться по мере необходимости и не будут считаться лишний раз:
count(g, v) if w[v] return d[v] else sum = 0 for c in g[v] sum += count(g, c) d[v] = sum w[v] = true return sum countPaths(g, s, t) d[s] = 1 w[s] = true answer = count(t) return answer
Значение функции
считается для каждой вершины один раз, а внутри нее рассматриваются все такие ребра . Всего таких ребер для всех вершин в графе , следовательно, время работы алгоритма в худшем случае оценивается как , где - количество вершин графа, - количество ребер.Пример работы
Рассмотрим пример работы алгоритма на следующем графе:
Изначально массивы
и инициализированы следующим образом:вершина | S | 1 | 2 | 3 | 4 | T |
w | true | false | false | false | false | false |
d | 1 | 0 | 0 | 0 | 0 | 0 |
Сначала функция
будет вызвана от вершины . Ответ для нее еще не посчитан ( ), следовательно будет вызвана от вершин и . Для вершины ответ также не посчитан ( ), следовательно будет вызвана уже для вершин и . А вот для них ответ мы уже можем узнать: для он равен , так как это - единствнная вершина, ребро из которой входит в нее. Непосредственно для ответ нам также известен. На текущий момент таблица будет выглядеть следующим образом:вершина | S | 1 | 2 | 3 | 4 | T |
w | true | false | true | false | false | false |
d | 1 | 0 | 1 | 0 | 0 | 0 |
Теперь мы знаем значения для вершин
и , что позволяет вычислить . Также обновим значения в массиве : .вершина | S | 1 | 2 | 3 | 4 | T |
w | true | false | true | true | false | false |
d | 1 | 0 | 1 | 2 | 0 | 0 |
В самом начале для вычисления
нам требовались значения и . Теперь нам известно значение , поэтому проследим за тем, как будет вычисляться . , но , следовательно значения и мы уже знаем, и нам необходимо вызвать . Ответ для этой вершины равен , так как это единственная вершина, ребро из которой входит в . Обновим соответствующие значения массивов и :вершина | S | 1 | 2 | 3 | 4 | T |
w | true | true | true | true | false | false |
d | 1 | 1 | 1 | 2 | 0 | 0 |
Теперь нам известны все три значения, требующиеся для вычисления ответа для вершины
. :вершина | S | 1 | 2 | 3 | 4 | T |
w | true | true | true | true | true | false |
d | 1 | 1 | 1 | 2 | 4 | 0 |
Наконец, вычислим
и обновим таблицы и :вершина | S | 1 | 2 | 3 | 4 | T |
w | true | true | true | true | true | true |
d | 1 | 1 | 1 | 2 | 4 | 6 |
Этот алгоритм позволяет вычислить количество путей от какой-либо вершины
не только до , но и для любой вершины, лежащей на любом из путей от до . Для этого достаточно взять значение в соответствующей ячейке .