Загальна кількість топологічних сортів


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]

Функція? Ціла програма? Або?
isaacg

@isaacg Або.
jimmy23013

Відповіді:


4

CJam - 25

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

З великою допомогою користувача23013 :)

Спробуйте в Інтернеті

Пояснення:

Загальний алгоритм такий самий, як у рішенні Python xnor .
Ключовим тут є 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(…)"

1
Відредаговано для явного дозволу списку вершин у вхідних даних. Зараз 25 байт .
jimmy23013

@ user23013 Що це за чаклунство? : o
кинути aditsu тому, що SE - EVIL

7

Пітона, 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.


+1 за те, що не використовується крайовий список.
jimmy23013

5

Математика, 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

Піт, 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] ]

Спробуйте тут.


@ user23013 Використовуються кількість і список ботів у виразі ^UGG, який генерує всі Gзаписи списку range(len(G)).
isaacg

Я мав на увазі, чи буде вона коротшою, якщо ви використовуєте [0, 1, ...]безпосередньо вхід?
jimmy23013

@ User23013 Ні, це буде такий же довжини: ^GlGпроти ^UGG.
isaacg

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]задовольняють усі ребра : позиція uв pменше, ніж позиція vв p. Це пряма реалізація визначення.

Редагувати: моя перша версія повернула самі топологічні сорти, а не скільки їх було. Виправлено це.

Правка II: не працювала для графіків із не підключеними вершинами. Виправлено це.


Я думаю додати тестовий випадок із лише вершинами, але не ребрами ...
jimmy23013

@ user23013: зараз працює для графіків з не підключеними вершинами. Це навіть стало коротшим.
німі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.