<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=94.180.143.174&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=94.180.143.174&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/94.180.143.174"/>
		<updated>2026-04-21T13:52:48Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%87%D0%B8%D1%81%D0%BB%D0%B5_%D0%BF%D1%83%D1%82%D0%B5%D0%B9_%D0%B2_%D0%B0%D1%86%D0%B8%D0%BA%D0%BB%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%BC_%D0%B3%D1%80%D0%B0%D1%84%D0%B5&amp;diff=68190</id>
		<title>Задача о числе путей в ациклическом графе</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%87%D0%B8%D1%81%D0%BB%D0%B5_%D0%BF%D1%83%D1%82%D0%B5%D0%B9_%D0%B2_%D0%B0%D1%86%D0%B8%D0%BA%D0%BB%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%BC_%D0%B3%D1%80%D0%B0%D1%84%D0%B5&amp;diff=68190"/>
				<updated>2019-01-07T17:53:14Z</updated>
		
		<summary type="html">&lt;p&gt;94.180.143.174: Была ошибка, из-за которой рекурсия не могла кончиться&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Задача&lt;br /&gt;
|definition = Задан [[Основные определения теории графов|ациклический граф]] &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; и две вершины &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Необходимо посчитать количество путей из вершины &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; в вершину &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; по рёбрам графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;|&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Решение задачи ==&lt;br /&gt;
&lt;br /&gt;
=== Перебор всех возможных путей ===&lt;br /&gt;
&lt;br /&gt;
Небольшая модификация алгоритма [[Обход в глубину, цвета вершин|обхода в глубину]]. Запустим обход в глубину от вершины &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. При каждом посещении вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; проверим, не является ли она искомой вершиной &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Если это так, то ответ увеличивается на единицу и обход прекращается. В противном случае производится запуск обхода в глубину для всех вершин, в которые есть ребро из &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, причем он производится независимо от того, были эти вершины посещены ранее, или нет.&lt;br /&gt;
&lt;br /&gt;
Функция &amp;lt;tex&amp;gt;\mathrm{countPaths(g, s, t)}&amp;lt;/tex&amp;gt; принимает граф &amp;lt;tex&amp;gt;g&amp;lt;/tex&amp;gt; в виде списка смежности, начальную вершину &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и конечную вершину &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 '''countPaths'''(g, v, t)&lt;br /&gt;
     '''if''' v == t&lt;br /&gt;
         '''return''' 1&lt;br /&gt;
     '''else'''&lt;br /&gt;
         s = 0&lt;br /&gt;
         '''for''' to '''in''' g[v]&lt;br /&gt;
             s += '''count'''(g, to, t)&lt;br /&gt;
         '''return''' s&lt;br /&gt;
&lt;br /&gt;
Время работы данного алгоритма в худшем случае &amp;lt;tex&amp;gt;O(Ans)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;Ans&amp;lt;/tex&amp;gt; — число путей в графе из &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Например, на следующем графе данный алгоритм будет иметь время работы &amp;lt;tex&amp;gt;O(2^{n/2})&amp;lt;/tex&amp;gt;. Если же использовать метод динамического программирования, речь о котором пойдет ниже, то асимптотику можно улучшить до &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Dp-countpaths-example.png‎|600px| Пример графа, на котором алгоритм имеет время работы &amp;lt;tex&amp;gt;O(2^{n/2})&amp;lt;/tex&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
=== Метод динамического программирования ===&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;P(v)&amp;lt;/tex&amp;gt; — число путей от вершины &amp;lt;tex&amp;gt; s &amp;lt;/tex&amp;gt; до вершины &amp;lt;tex&amp;gt; v &amp;lt;/tex&amp;gt;.&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;P(v)&amp;lt;/tex&amp;gt; зависит только от вершин, ребра из которых входят в &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt;P(v) = \sum\limits_{c}P(c)&amp;lt;/tex&amp;gt; таких &amp;lt;tex&amp;gt;c&amp;lt;/tex&amp;gt;, что есть ребро из &amp;lt;tex&amp;gt;c&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;. Мы свели нашу задачу к меньшим подзадачам, причем мы также знаем, что &amp;lt;tex&amp;gt;P(s) = 1&amp;lt;/tex&amp;gt;. Это позволяет решить задачу методом динамического программирования.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; — стартовая вершина, а &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; — конечная, для нее и посчитаем ответ. Будем поддерживать массив &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;d[v]&amp;lt;/tex&amp;gt; — число путей из вершины &amp;lt;tex&amp;gt; s &amp;lt;/tex&amp;gt; до вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и массив &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;w[v] = true&amp;lt;/tex&amp;gt;, если ответ для вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; уже посчитан, и &amp;lt;tex&amp;gt;w[v] = false&amp;lt;/tex&amp;gt; в противном случае. Изначально &amp;lt;tex&amp;gt;w[i] = false&amp;lt;/tex&amp;gt; для всех вершин &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, кроме &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;d[s] = 1&amp;lt;/tex&amp;gt;. Функция &amp;lt;tex&amp;gt;\mathrm{count(v)}&amp;lt;/tex&amp;gt; будет возвращать ответ для вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;. Удобнее всего это реализовать в виде рекурсивной функции с запоминанием. В этом случае значения массива &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; будут вычисляться по мере необходимости и не будут считаться лишний раз: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt; count(v) = \left \{ &lt;br /&gt;
\begin{array}{ll}&lt;br /&gt;
 d[v], &amp;amp; w[v]=true \\&lt;br /&gt;
 \sum\limits_{c|cv \in E}count(c), &amp;amp; w[v]=false&lt;br /&gt;
 \end{array}&lt;br /&gt;
 \right.&lt;br /&gt;
&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 '''count'''(g, v)&lt;br /&gt;
     '''if''' w[v]&lt;br /&gt;
         return d[v]&lt;br /&gt;
     '''else'''&lt;br /&gt;
         sum = 0&lt;br /&gt;
         w[v] = ''true''&lt;br /&gt;
         '''for''' c '''in''' g[v]&lt;br /&gt;
             sum += '''count'''(g, c)&lt;br /&gt;
         d[v] = sum&lt;br /&gt;
         '''return''' sum&lt;br /&gt;
 &lt;br /&gt;
 '''countPaths'''(g, s, t)&lt;br /&gt;
     d[s] = 1&lt;br /&gt;
     w[s] = ''true''&lt;br /&gt;
     answer = '''count'''(t)&lt;br /&gt;
     '''return''' answer&lt;br /&gt;
&lt;br /&gt;
Значение функции &amp;lt;tex&amp;gt;\mathrm{count(v)}&amp;lt;/tex&amp;gt; считается для каждой вершины один раз, а внутри нее рассматриваются все такие ребра &amp;lt;tex&amp;gt;\{e\ |\ end(e) = v\}&amp;lt;/tex&amp;gt;. Всего таких ребер для всех вершин в графе &amp;lt;tex&amp;gt;O(E)&amp;lt;/tex&amp;gt;, следовательно, время работы алгоритма в худшем случае оценивается как &amp;lt;tex&amp;gt;O(V+E)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;V&amp;lt;/tex&amp;gt; — число вершин графа, &amp;lt;tex&amp;gt;E&amp;lt;/tex&amp;gt; — число ребер.&lt;br /&gt;
&lt;br /&gt;
== Пример работы ==&lt;br /&gt;
&lt;br /&gt;
Рассмотрим пример работы алгоритма на следующем графе:&lt;br /&gt;
&lt;br /&gt;
[[Файл: count-path-graph-example.png|thumb|300px ]]&lt;br /&gt;
&lt;br /&gt;
Изначально массивы &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; инициализированы следующим образом:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; cellpadding=&amp;quot;4&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse: collapse;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''вершина''' || S || 1 || 2 || 3 || 4 || T&lt;br /&gt;
|-&lt;br /&gt;
| '''w''' || true || false || false || false || false || false&lt;br /&gt;
|-&lt;br /&gt;
| '''d''' || 1 || 0 || 0 || 0 || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
Сначала функция &amp;lt;tex&amp;gt;\mathrm{count}&amp;lt;/tex&amp;gt; будет вызвана от вершины &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Ответ для нее еще не посчитан (&amp;lt;tex&amp;gt;w[T] = false&amp;lt;/tex&amp;gt;), следовательно &amp;lt;tex&amp;gt;\mathrm{count}&amp;lt;/tex&amp;gt; будет вызвана от вершин &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;. Для вершины &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; ответ также не посчитан (&amp;lt;tex&amp;gt;w[3] = false&amp;lt;/tex&amp;gt;), следовательно &amp;lt;tex&amp;gt;\mathrm{count}&amp;lt;/tex&amp;gt; будет вызвана уже для вершин &amp;lt;tex&amp;gt;2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;. А вот для них ответ мы уже можем узнать: для &amp;lt;tex&amp;gt;2&amp;lt;/tex&amp;gt; он равен &amp;lt;tex&amp;gt;d[S]&amp;lt;/tex&amp;gt;, так как это &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; — единственная вершина, ребро из которой входит в нее. Непосредственно для &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; ответ нам также известен. На текущий момент таблица будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; cellpadding=&amp;quot;4&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse: collapse;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''вершина''' || S || 1 || 2 || 3 || 4 || T&lt;br /&gt;
|-&lt;br /&gt;
| '''w''' || true || false || '''true''' || false || false || false&lt;br /&gt;
|-&lt;br /&gt;
| '''d''' || 1 || 0 || '''1''' || 0 || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Теперь мы знаем значения для вершин &amp;lt;tex&amp;gt;2&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;, что позволяет вычислить &amp;lt;tex&amp;gt;d[3] = d[2] + d[S] = 2&amp;lt;/tex&amp;gt;. Также обновим значения в массиве &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;: &amp;lt;tex&amp;gt;w[3] = true&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; cellpadding=&amp;quot;4&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse: collapse;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''вершина''' || S || 1 || 2 || 3 || 4 || T&lt;br /&gt;
|-&lt;br /&gt;
| '''w''' || true || false || true || '''true''' || false || false&lt;br /&gt;
|-&lt;br /&gt;
| '''d''' || 1 || 0 || 1 || '''2''' || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
В самом начале для вычисления &amp;lt;tex&amp;gt;d[T]&amp;lt;/tex&amp;gt; нам требовались значения &amp;lt;tex&amp;gt;d[3]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;d[4]&amp;lt;/tex&amp;gt;. Теперь нам известно значение &amp;lt;tex&amp;gt;d[3]&amp;lt;/tex&amp;gt;, поэтому проследим за тем, как будет вычисляться &amp;lt;tex&amp;gt;d[4]&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;\mathrm{d[4] = count(3) + count(2) + count(1)}&amp;lt;/tex&amp;gt;, но &amp;lt;tex&amp;gt;w[3] = true, w[2] = true&amp;lt;/tex&amp;gt;, следовательно значения &amp;lt;tex&amp;gt;d[3]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;d[2]&amp;lt;/tex&amp;gt; мы уже знаем, и нам необходимо вызвать &amp;lt;tex&amp;gt;\mathrm{count(1)}&amp;lt;/tex&amp;gt;. Ответ для этой вершины равен &amp;lt;tex&amp;gt;d[S]&amp;lt;/tex&amp;gt;, так как это единственная вершина, ребро из которой входит в &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Обновим соответствующие значения массивов &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; cellpadding=&amp;quot;4&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse: collapse;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''вершина''' || S || 1 || 2 || 3 || 4 || T&lt;br /&gt;
|-&lt;br /&gt;
| '''w''' || true || '''true''' || true || true || false || false&lt;br /&gt;
|-&lt;br /&gt;
| '''d''' || 1 || '''1''' || 1 || 2 || 0 || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Теперь нам известны все три значения, требующиеся для вычисления ответа для вершины &amp;lt;tex&amp;gt;4&amp;lt;/tex&amp;gt;. &amp;lt;tex&amp;gt;d[4] = d[3] + d[2] + d[1] = 2 + 1 + 1 = 4&amp;lt;/tex&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; cellpadding=&amp;quot;4&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse: collapse;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''вершина''' || S || 1 || 2 || 3 || 4 || T&lt;br /&gt;
|-&lt;br /&gt;
| '''w''' || true || true || true || true || '''true''' || false&lt;br /&gt;
|-&lt;br /&gt;
| '''d''' || 1 || 1 || 1 || 2 || '''4''' || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Наконец, вычислим &amp;lt;tex&amp;gt;d[T] = d[3] + d[4] = 2 + 4 = 6&amp;lt;/tex&amp;gt; и обновим таблицы &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; cellpadding=&amp;quot;4&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;border-collapse: collapse;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''вершина''' || S || 1 || 2 || 3 || 4 || T&lt;br /&gt;
|-&lt;br /&gt;
| '''w''' || true || true || true || true || true || '''true'''&lt;br /&gt;
|-&lt;br /&gt;
| '''d''' || 1 || 1 || 1 || 2 || 4 || '''6'''&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Этот алгоритм позволяет вычислить количество путей от какой-либо вершины &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; не только до &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;, но и для любой вершины, лежащей на любом из путей от &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Для этого достаточно взять значение в соответствующей ячейке &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Динамическое программирование]]&lt;br /&gt;
* [[Кратчайший путь в ациклическом графе]]&lt;br /&gt;
* [[Задача о расстановке знаков в выражении]]&lt;br /&gt;
* [[Задача о порядке перемножения матриц]]&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
* Акулич И.Л. Глава 4. Задачи динамического программирования // Математическое программирование в примерах и задачах. — М.: Высшая школа, 1986. — 319 с. — ISBN 5-06-002663-9..&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Динамическое программирование]]&lt;/div&gt;</summary>
		<author><name>94.180.143.174</name></author>	</entry>

	</feed>