Задача о числе путей в ациклическом графе — различия между версиями
Nafanya (обсуждение | вклад) |
Nafanya (обсуждение | вклад) |
||
Строка 53: | Строка 53: | ||
'''else''' | '''else''' | ||
sum = 0 | sum = 0 | ||
− | '''for'''(всех <tex>c</tex> смежных с <tex>v</tex>) | + | '''for'''(всех <tex>c</tex> смежных с <tex>v</tex>) |
sum += '''count'''(c) | sum += '''count'''(c) | ||
d[v] = sum | d[v] = sum | ||
Строка 59: | Строка 59: | ||
'''return''' sum | '''return''' sum | ||
− | '''countPaths'''(s, t) | + | '''countPaths'''(s, t) |
d[s] = 1 | d[s] = 1 | ||
w[s] = true | w[s] = true | ||
Строка 74: | Строка 74: | ||
[[Файл: count-path-graph-example.png|thumb|300px| description ]] | [[Файл: count-path-graph-example.png|thumb|300px| description ]] | ||
+ | Изначально массивы <tex>d</tex> и <tex>w</tex> инициализированы следующим образом: | ||
+ | {| class="wikitable" cellpadding="4" border="1" style="border-collapse: collapse;" | ||
+ | |- | ||
+ | | '''вершина''' || S || 1 || 2 || 3 || 4 || T | ||
+ | |- | ||
+ | | '''w''' || true || false || false || false || false || false | ||
+ | |- | ||
+ | | '''d''' || 1 || 0 || 0 || 0 || 0 || 0 | ||
+ | |} | ||
+ | Сначала функция <tex>count</tex> будет вызвана от вершины <tex>T</tex>. Ответ для нее еще не посчитан (<tex>w[T] = false</tex>), следовательно <tex>count</tex> будет вызвана от вершин <tex>3</tex> и <tex>4</tex>. Для вершины <tex>3</tex> ответ также не посчитан (<tex>w[3] = false</tex>), следовательно <tex>count</tex> будет вызвана уже для вершин <tex>2</tex> и <tex>S</tex>. А вот для них ответ мы уже можем узнать: для <tex>2</tex> он равен <tex>d[S]</tex>, так как это <tex>S</tex> - единствнная вершина, ребро из которой входит в нее. Непосредственно для <tex>S</tex> ответ нам также известен. На текущий момент таблица будет выглядеть следующим образом: | ||
+ | |||
+ | {| class="wikitable" cellpadding="4" border="1" style="border-collapse: collapse;" | ||
+ | |- | ||
+ | | '''вершина''' || S || 1 || 2 || 3 || 4 || T | ||
+ | |- | ||
+ | | '''w''' || true || false || '''true''' || false || false || false | ||
+ | |- | ||
+ | | '''d''' || 1 || 0 || '''1''' || 0 || 0 || 0 | ||
+ | |} | ||
+ | |||
+ | Теперь мы знаем значения для вершин <tex>2</tex> и <tex>S</tex>, что позволяет вычислить <tex>d[3] = d[2] + d[S] = 2</tex>. Также обновим значения в массиве <tex>w</tex>: <tex>w[3] = true</tex>. | ||
+ | |||
+ | {| class="wikitable" cellpadding="4" border="1" style="border-collapse: collapse;" | ||
+ | |- | ||
+ | | '''вершина''' || S || 1 || 2 || 3 || 4 || T | ||
+ | |- | ||
+ | | '''w''' || true || false || true || '''true''' || false || false | ||
+ | |- | ||
+ | | '''d''' || 1 || 0 || 1 || '''2''' || 0 || 0 | ||
+ | |} | ||
+ | |||
+ | В самом начале для вычисления <tex>d[T]</tex> нам требовались значения <tex>d[3]</tex> и <tex>d[4]</tex>. Теперь нам известно значение <tex>d[3]</tex>, поэтому проследим за тем, как будет вычисляться <tex>d[4]</tex>. <tex>d[4] = count(3) + count(2) + count(1)</tex>, но <tex>w[3] = true, w[2] = true</tex>, следовательно значения <tex>d[3]</tex> и <tex>d[2]</tex> мы уже знаем, и нам необходимо вызвать <tex>count(1)</tex>. Ответ для этой вершины равен <tex>d[S]</tex>, так как это единственная вершина, ребро из которой входит в <tex>1</tex>. Обновим соответствующие значения массивов <tex>d</tex> и <tex>w</tex>: | ||
+ | |||
+ | {| class="wikitable" cellpadding="4" border="1" style="border-collapse: collapse;" | ||
+ | |- | ||
+ | | '''вершина''' || S || 1 || 2 || 3 || 4 || T | ||
+ | |- | ||
+ | | '''w''' || true || '''true''' || true || true || false || false | ||
+ | |- | ||
+ | | '''d''' || 1 || '''1''' || 1 || 2 || 0 || 0 | ||
+ | |} | ||
+ | |||
+ | Теперь нам известны все три значения, требующиеся для вычисления ответа для вершины <tex>4</tex>. <tex>d[4] = d[3] + d[2] + d[1] = 2 + 1 + 1 = 4</tex>: | ||
+ | |||
+ | {| class="wikitable" cellpadding="4" border="1" style="border-collapse: collapse;" | ||
+ | |- | ||
+ | | '''вершина''' || S || 1 || 2 || 3 || 4 || T | ||
+ | |- | ||
+ | | '''w''' || true || true || true || true || '''true''' || false | ||
+ | |- | ||
+ | | '''d''' || 1 || 1 || 1 || 2 || '''4''' || 0 | ||
+ | |} | ||
+ | |||
+ | Наконец, вычислим <tex>d[T] = d[3] + d[4] = 2 + 4 = 6</tex> и обновим таблицы <tex>d</tex> и<tex>w</tex>: | ||
+ | |||
+ | {| class="wikitable" cellpadding="4" border="1" style="border-collapse: collapse;" | ||
+ | |- | ||
+ | | '''вершина''' || S || 1 || 2 || 3 || 4 || T | ||
+ | |- | ||
+ | | '''w''' || true || true || true || true || true || '''true''' | ||
+ | |- | ||
+ | | '''d''' || 1 || 1 || 1 || 2 || 4 || '''6''' | ||
+ | |} | ||
+ | |||
+ | Этот алгоритм позволяет вычислить количество путей от какой-либо вершины <tex>S</tex> не только до <tex>T</tex>, но и для любой вершины, лежащей на любом из путей от <tex>S</tex> до <tex>T</tex>. Для этого достаточно взять значение в соответствующей ячейке <tex>d</tex>. | ||
[[Категория:Дискретная математика и алгоритмы]] | [[Категория:Дискретная математика и алгоритмы]] | ||
[[Категория:Динамическое программирование]] | [[Категория:Динамическое программирование]] |
Версия 18:26, 29 декабря 2013
Задача о числе путей в ациклическом графе - одна из классических задач на тему динамического программирования. В этой задаче нам дан ациклический граф
и две вершины и . Необходимо посчитать количество путей из вершины в вершину по рёбрам графа .Число таких путей может быть велико даже на небольших графах, поэтому перебор всех возможных вариантов займет много времени. Однако, данную задачу можно решить гораздо быстрее с помощью динамики.
Содержание
Решение задачи
Перебор всех возможных путей
Небольшая модификация алгоритма обхода в глубину. Запустим обход в глубину от вершины . При каждом посещении вершины проверим, не является ли она искомой вершиной . Если это так, то ответ увеличивается на единицу и обход прекращается. В противном случае производится запуск обхода в глубину от всех вершин, ребра в которые выходят из , причем он производится независимо от того, были ли эти вершины посещены ранее, или нет.
Функция
принимает начальную вершину и конечную вершину . В глобальной переменной содержится ответ.
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
Значение функции
считается для каждой вершины один раз, а внутри нее рассматриваются все такие ребра . Всего таких ребер для всех вершин в графе , следовательно, время работы алгоритма в худшем случае оценивается как , где - количество вершин графа, - количество ребер.Пример работы
Рассмотрим пример работы алгоритма на следующем графе:
Изначально массивы
и инициализированы следующим образом:вершина | 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 |
Этот алгоритм позволяет вычислить количество путей от какой-либо вершины
не только до , но и для любой вершины, лежащей на любом из путей от до . Для этого достаточно взять значение в соответствующей ячейке .