Задача о числе путей в ациклическом графе — различия между версиями
Kseniia (обсуждение | вклад) (шапка) |
м (rollbackEdits.php mass rollback) |
||
(не показано 6 промежуточных версий 4 участников) | |||
Строка 1: | Строка 1: | ||
{{Задача | {{Задача | ||
− | |definition = Задан [ | + | |definition = Задан [[Основные определения теории графов|ациклический граф]] <tex>G</tex> и две вершины <tex>s</tex> и <tex>t</tex>. Необходимо посчитать количество путей из вершины <tex>s</tex> в вершину <tex>t</tex> по рёбрам графа <tex>G</tex>. |
}} | }} | ||
</noinclude> | </noinclude> | ||
Строка 28: | Строка 28: | ||
s = 0 | s = 0 | ||
'''for''' to '''in''' g[v] | '''for''' to '''in''' g[v] | ||
− | s += ''' | + | s += '''countPaths'''(g, to, t) |
'''return''' s | '''return''' s | ||
Строка 47: | Строка 47: | ||
\begin{array}{ll} | \begin{array}{ll} | ||
d[v], & w[v]=true \\ | d[v], & w[v]=true \\ | ||
− | \sum\limits_{c}count(c) | + | \sum\limits_{c|cv \in E}count(c), & w[v]=false |
\end{array} | \end{array} | ||
\right. | \right. | ||
Строка 57: | Строка 57: | ||
'''else''' | '''else''' | ||
sum = 0 | sum = 0 | ||
+ | w[v] = ''true'' | ||
'''for''' c '''in''' g[v] | '''for''' c '''in''' g[v] | ||
sum += '''count'''(g, c) | sum += '''count'''(g, c) | ||
d[v] = sum | d[v] = sum | ||
− | |||
'''return''' sum | '''return''' sum | ||
Текущая версия на 19:37, 4 сентября 2022
Задача: |
Задан ациклический граф и две вершины и . Необходимо посчитать количество путей из вершины в вершину по рёбрам графа . |
Содержание
Решение задачи
Перебор всех возможных путей
Небольшая модификация алгоритма обхода в глубину. Запустим обход в глубину от вершины . При каждом посещении вершины проверим, не является ли она искомой вершиной . Если это так, то ответ увеличивается на единицу и обход прекращается. В противном случае производится запуск обхода в глубину для всех вершин, в которые есть ребро из , причем он производится независимо от того, были эти вершины посещены ранее, или нет.
Функция
принимает граф в виде списка смежности, начальную вершину и конечную вершину .countPaths(g, v, t) if v == t return 1 else s = 0 for to in g[v] s += countPaths(g, to, t) return s
Время работы данного алгоритма в худшем случае
, где — число путей в графе из в . Например, на следующем графе данный алгоритм будет иметь время работы . Если же использовать метод динамического программирования, речь о котором пойдет ниже, то асимптотику можно улучшить до .Метод динамического программирования
Пусть
— число путей от вершины до вершины . Тогда зависит только от вершин, ребра из которых входят в . Тогда таких , что есть ребро из в . Мы свели нашу задачу к меньшим подзадачам, причем мы также знаем, что . Это позволяет решить задачу методом динамического программирования.Псевдокод
Пусть
— стартовая вершина, а — конечная, для нее и посчитаем ответ. Будем поддерживать массив , где — число путей из вершины до вершины и массив , где , если ответ для вершины уже посчитан, и в противном случае. Изначально для всех вершин , кроме , а . Функция будет возвращать ответ для вершины . Удобнее всего это реализовать в виде рекурсивной функции с запоминанием. В этом случае значения массива будут вычисляться по мере необходимости и не будут считаться лишний раз:
count(g, v) if w[v] return d[v] else sum = 0 w[v] = true for c in g[v] sum += count(g, c) d[v] = sum 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 |
Этот алгоритм позволяет вычислить количество путей от какой-либо вершины
не только до , но и для любой вершины, лежащей на любом из путей от до . Для этого достаточно взять значение в соответствующей ячейке .См. также
- Динамическое программирование
- Кратчайший путь в ациклическом графе
- Задача о расстановке знаков в выражении
- Задача о порядке перемножения матриц
Источники информации
- Акулич И.Л. Глава 4. Задачи динамического программирования // Математическое программирование в примерах и задачах. — М.: Высшая школа, 1986. — 319 с. — ISBN 5-06-002663-9..