Общее количество топологических сортов

11

Для данного DAG (направленного ациклического графа) каждый из его топологических сортов является перестановкой всех вершин, где для каждого ребра (u, v) в DAG, u появляется перед v в перестановке.

Ваша задача - вычислить общее количество топологических видов данного DAG.

правила

  • Вы можете использовать любой формат для представления графа, например матрицу смежности, список смежности или список ребер, если вы не выполняете полезные вычисления в своей кодировке. Вы также можете иметь такие вещи, как количество вершин или список вершин на входе, если они полезны.
  • Вы можете предположить, что график на входе всегда DAG (не имеет циклов).
  • Ваша программа должна работать теоретически для любого входа. Но он может потерпеть неудачу, если переполнит базовый целочисленный тип в вашем языке.
  • Имена вершин могут быть любыми последовательными значениями в любом типе. Например: числа, начинающиеся с 0 или 1. (И, конечно, только если вы не храните код в этом номере.)
  • Это код-гольф. Самый короткий код выигрывает.

пример

Это один и тот же вход в разных форматах. Ваша программа не должна принимать все из них. Вершины всегда целые, начиная с 0.

Adjacency list:
[ [1 2 3 5] [2 4] [] [2] [] [3] ]
Adjacency matrix:
[ [0 1 1 1 0 1] [0 0 1 0 1 0] [0 0 0 0 0 0] [0 0 1 0 0 0] [0 0 0 0 0 0] [0 0 0 1 0 0] ]
Edge list:
6 [ [0 1] [0 2] [0 3] [0 5] [1 2] [1 4] [3 2] [5 3] ]

Это график, показанный на этом изображении:

Пример графика

Выход должен быть:

9

Топологическими видами являются:

[0 1 4 5 3 2]
[0 1 5 4 3 2]
[0 1 5 3 4 2]
[0 1 5 3 2 4]
[0 5 1 4 3 2]
[0 5 1 3 4 2]
[0 5 1 3 2 4]
[0 5 3 1 4 2]
[0 5 3 1 2 4]
jimmy23013
источник
Функция? Вся программа? Или?
Исаак
@isaacg Либо.
jimmy23013

Ответы:

4

CJam - 25

q~{_f{1$-_j@j@&!*}_!+:+}j

С большой помощью user23013 :)

Попробуйте онлайн

Объяснение:

Общий алгоритм такой же, как и в решении xnor Python .
Ключевым моментом здесь является jоператор, который делает запомненную рекурсию. Он принимает параметр, значение или массив для начальных значений (как в f (0), f (1) и т. Д.) И блок для определения рекурсии. jОператор снова используется внутри блока для ведения рекурсивных (и memoized) вызовы в одном блоке. Его также можно использовать с несколькими параметрами, но здесь это не так.
Отличным нововведением user23013 является использование j с различными типами данных, используя список смежности в качестве массива начальных значений.

q~             read and evaluate the input (vertex list followed by adjacency list)
{…}j           run the block on the vertex list, doing memoized recursion
                and using the adjacency list for initial values
    _          copy the vertex list
    f{…}       for each vertex and the vertex list
        1$-    copy the vertex and remove it from the list
                Python: "V-{v}"
        _j     copy the reduced list and call the j block recursively
                this solves the problem for the reduced vertex list
                Python: "f(G,V-{v})"
        @j     bring the vertex to the top of the stack and call the j block recursively
                in this case, it's called with a vertex rather than a list
                and the memoized value is instantly found in the list of initial values
                effectively, this gets the list of vertices adjacent to the current vertex
                Python: "G[v]"
        @&     bring the reduced list to the top of the stack and intersect
        !*     multiply the number of topological sorts of the reduced vertex list
                with 1 if the intersection was empty and 0 if not
                Python: equivalent to "*(V-G[v]==V)"
               after this loop we get an array of sub-results for the reduced vertex lists
    _!+        add 1 or 0 to the array if the array was empty or not
                because we want to get 1 for the empty array
                Python: equivalent to "V<{0}or"
    :+         add the numbers in the array
                Python: "sum(…)"
уйти, потому что SE это зло
источник
1
Отредактировано, чтобы явно разрешить список вершин во входных данных. Теперь 25 байтов .
Джимми23013
@ user23013 Что это за колдовство? : o
aditsu ушел, потому что SE злой
7

Python, 58

f=lambda G,V:V<{0}or sum(f(G,V-{v})*(V-G[v]==V)for v in V)

Входные данные состоят из словаря смежности Gи набора вершин V.

G = {0:{1,2,3,5}, 1:{2,4}, 2:set(), 3:{2}, 4:set(), 5:{3}, 6:set()}
V = {0,1,2,3,4,5}

Код рекурсивный. Набор Vхранит все узлы, которые все еще нуждаются в посещении. Для каждого потенциального следующего узла мы проверяем его пригодность, проверяя, указывают ли на него оставшиеся вершины, V-G[v]==Vпроверяя его Vи G[v]не пересекаясь. Для всех подходящих таких вершин мы добавляем количество топологических сортировок с удаленными ими. В качестве базового случая пустой набор дает 1.

XNOR
источник
+1 за неиспользование списка ребер.
jimmy23013
5

Mathematica, 80 57 51 байт

Count[Permutations@#,l_/;l~Subsets~{2}~SubsetQ~#2]&

Очень прямолинейная реализация определения. Я просто генерирую все перестановки и подсчитываю, сколько из них допустимо. Чтобы проверить правильность перестановки, я получаю все пары вершин в перестановке. Удобно, что Subsets[l,{2}]не только дает мне все пары, но и поддерживает порядок, в котором они находятся l- только то, что мне нужно.

Выше приведена функция, которая ожидает список вершин и список ребер, как

f[{1, 2, 3, 4, 5, 6}, {{1, 2}, {1, 3}, {1, 4}, {1, 6}, {2, 3}, {2, 5}, {4, 3}, {6, 4}}]

если вы вызываете функцию f.

Я попробую сыграть в гольф, или, возможно, воспользуюсь другим подходом позже.

Мартин Эндер
источник
2

Pyth, 27 байт

Mlf!sm}_dHfq2lYyTfqSZUZ^UGG

Определяет функцию 2 входа g. Первый вход - количество вершин, второй - список направленных ребер.

Тестировать:

Code:
Mlf!sm}_dHfq2lYyTfqSZUZ^UGGghQeQ

Input:
6, [ [0, 1], [0, 2], [0, 3], [0, 5], [1, 2], [1, 4], [3, 2], [5, 3] ]

Попробуй это здесь.

isaacg
источник
@ user23013 В выражении используются счетчик и список Ботов ^UGG, который генерирует все Gсписки записей range(len(G)).
Исаак
Я имел в виду, будет ли оно короче, если вы будете использовать [0, 1, ...]непосредственно на входе?
jimmy23013
@ user23013 Нет, это будет такой же длины: ^GlGпротив ^UGG.
Исаак
2

Haskell, 102 107 100 89 85 байт

import Data.List
(%)=elemIndex
n#l=sum[1|p<-permutations[0..n],and[u%p<v%p|[u,v]<-l]]

В качестве входных данных используется наибольшее число вершин (начиная с 0) и список ребер, где ребро представляет собой список из двух элементов. Пример использования:5 # [[0,1], [0,2], [0,3], [0,5], [1,2], [1,4], [3,2], [5,3]]

Как это работает: подсчитайте все перестановки pвершин, которым [u,v]удовлетворяют все ребра : положение uin pменьше, чем положение vin p. Это прямая реализация определения.

Изменить: моя первая версия вернула топологические сортировки сами, а не сколько их есть. Починил это.

Редактировать II: не работал для графов с несвязанными вершинами. Починил это.

Ними
источник
Я подумываю добавить тестовый пример только с вершинами, но не с ребрами ...
jimmy23013
@ user23013: теперь работает для графов с несвязанными вершинами. Это даже стало короче.
Ними