Policz drzewa

11

Drzewa jest podłączone, nieukierunkowane wykres bez cykli. Twoim zadaniem jest policzyć, ile jest różnych drzew o danej liczbie wierzchołków.

Dwa drzewa są uważane za odrębne, jeśli nie są izomorficzne . Dwa wykresy są izomorficzne, jeśli ich odpowiednie wierzchołki można sparować w taki sposób, że istnieje krawędź między dwoma wierzchołkami na jednym wykresie tylko wtedy, gdy istnieje krawędź między wierzchołkami sparowanymi z tymi wierzchołkami na drugim wykresie. Aby uzyskać pełniejszy opis, patrz powyższy link.

Aby zobaczyć, jak wyglądają wszystkie wyraźne drzewa o rozmiarach od 1 do 6, spójrz tutaj .

Seria, którą próbujesz wydrukować, to A000055 w OEIS.

Ograniczenie : Twoje rozwiązanie musi zająć w minutach lub mniej, aby uruchomić się na danych wejściowych 6. Nie ma to na celu eliminacji wykładniczych algorytmów czasu, ale ma na celu wyeliminowanie podwójnie wykładniczych algorytmów czasu, takich jak brutalne wymuszanie na wszystkich zestawach krawędzi.

Dane wejściowe: dowolna liczba całkowita nieujemna.

Dane wejściowe można wprowadzać dowolnymi standardowymi środkami, w tym STDIN, parametr wiersza poleceń, wejście funkcji itp.

Dane wyjściowe: liczba odrębnych drzew z tyloma wierzchołkami co dane wejściowe.

Wyjście może odbywać się dowolnymi standardowymi środkami, w tym STDOUT, powrót funkcji itp.

Przykłady: 0, 1, 2, 3, 4, 5, 6, 7 powinien wrócić 1, 1, 1, 1, 2, 3, 6, 11.

Punktacja: Kod golfowy, bajty. Niech wygra najkrótszy kod!

Standardowe luki zabronione.

isaacg
źródło

Odpowiedzi:

3

CJam (69 bajtów)

]qi:X,{1e|,:):N{N\f{1$%!*}W$.*:+}%1$W%.*:+N,/+}/W\+_1,*X=\_W%.*:+-Y/z

Demo online

Wyjaśnienie

Podstawową ideą jest wdrożenie funkcji generowania opisanej w OEIS. Wejście to paskudny przypadek specjalny, ale ostatnie poprawki, które wprowadziłem, zakończyły się produkcją - 101 dla tego przypadku, więc (dla wartości bezwzględnej) porządkuje to. To najdziwniejsza sztuczka tutaj.z

.*:+ powtarza się trzy razy i wygląda na to, że zapisałby bajt, jeśli zostałby wyodrębniony jako {.*:+}:F~ . To jednak zrywa ze specjalnym przypadkiem , ponieważ w ogóle nie wykonuje pętli zewnętrznej.0


Używamy pomocniczej funkcji generującej dla A000081 , której warunki mają powtarzalność

a[0] = 0
a[1] = 1
For n >= 1, a[n+1] = (sum_{k=1}^n a[n-k+1] * sum_{d|k} d * a[d]) / n

Jestem pewien, że niektóre języki mają wbudowane odwrotne przekształcenie Mobiusa , ale CJam nie; najlepszym rozwiązaniem znalazłem jest budowanie tablicy odwzorowania d do , a następnie wykonać mnożenie punktowo użyciu . Zauważ, że tutaj wygodnie jest zbudować wartość początkową od indeksu 1, ponieważ chcemy uniknąć dzielenia przez zero podczas ustawiania wag. Należy również zauważyć, że jeśli dwie tablice dostarczone do operacji punktowej nie są tej samej długości, wówczas wartości z dłuższej pozostają niezmienione: dlatego musimy przyjąć pierwsze k wyrażeńdkd×a[d]dk % d == 0 ? d : 0a.*ak lub ustaw tablicę wag w górę do n . Ten ostatni wydaje się krótszy. Więc ta odwrotna transformacja Möbiusa odpowiadaanN\f{1$%!*}W$.*:+

Jeżeli nazywamy wyniku odwrotnego przekształcenia Möbiusa M, mamy

a[n+1]=1nk=1na[nk+1]×M[k]

aM1nn+1a od 1 zamiast 0. Mamy teraz stanowiły

 qi:X,{   ,:):N{N\f{1$%!*}W$.*:+}%1$W%.*:+N,/+}/

Punkt pomocniczej funkcji generującej jest określony przez sekcję formuły A000055:

G.f.: A(x) = 1 + T(x) - T^2(x)/2 + T(x^2)/2,
where T(x) = x + x^2 + 2*x^3 + ... is the g.f. for A000081.

a

[x=0]+a[x]+12(a[x/2]i=0na[i]×a[ni])

a[x/2]x otrzymujemy 0. Najkrótsza droga Znalazłem na to jest, aby napompować zerami ( 1,*), a następnie podjąć elementu X-tym ( X=).

0\+a[0]=0X=0W\+2a[x]+i=0na[i]×a[ni]2a[x]

Więc wyjaśniliśmy

 qi:X,{   ,:):N{N\f{1$%!*}W$.*:+}%1$W%.*:+N,/+}/W\+_1,*X=\_W%.*:+-Y/

1]N=1

1]qi:X,1>{ ... }/

X=0a[-1 1]0[x=0] ). Naprawiliśmy to dla trzech znaków albo jako finał, X!+albo przy użyciu standardowej techniki „rezerwowej” jako 1e|.

a1N=0

]qi:X,{ ... /+}/

oczywiście daje podział przez zero. Ale jeśli spróbujemy

]qi:X,{1e| ... /+}/

to działa. Dostajemy

             e# Stack: [] 0
1e|          e# Stack: [] 1
,:):N        e# Stack: [] [1]
{            e# We only execute this loop once
  N\f{1$%!*} e#   1 divides 1, so stack: [] [1]
  W$.*       e#   Remember: if the two arrays supplied to the pointwise operation
             e#   are not the same length then the values from the longer one are
             e#   left untouched. Stack: [] [1]
  :+         e#   Fold over a singleton. Stack: [] 1
}%           e# And that was a map, so stack: [] [1]
1$W%.*:+     e# Another [1] [] .*:+, giving the same result: 1
N,/          e# 1 / 1 = 1
+            e# And we append 1 to a giving [1]

co daje dokładnie taką wartość, jakiej potrzebujemy.

X=01[-1](112(1×1))=10111z

Peter Taylor
źródło
1

Pyth, 35 bajtów

l{m`SmSSMcXdUQk2.pQusm+L,dhHGhHtQ]Y

Demonstracja.

Ten program można podzielić na dwie części: Najpierw generujemy wszystkie możliwe drzewa, a następnie usuwamy duplikaty.

Ten kod generuje drzew: usm+L,dhHGhHtQ]Y. Drzewa są reprezentowane jako skonkatowana lista krawędzi, mniej więcej tak:

[0, 1, 0, 2, 2, 3, 1, 4]

Każda liczba oznacza wierzchołek, a każda dwie liczby stanowią krawędź. Jest on budowany przez wielokrotne dodawanie krawędzi między każdym możliwym wierzchołkiem, który już istnieje, i tym, który jest nowo utworzony, i dodawanie tego do każdego możliwego drzewa z poprzedniego kroku. Generuje to wszystkie możliwe drzewa, ponieważ wszystkie drzewa można wygenerować poprzez wielokrotne dodawanie jednego wierzchołka i jego krawędzi do istniejącego drzewa. Powstaną jednak drzewa izomorficzne.

Następnie dla każdego drzewa wykonujemy każde możliwe znakowanie. Odbywa się to poprzez mapowanie wszystkich możliwych permutacji wierzchołków ( m ... .pQ), a następnie przejście drzewa ze standardowego uporządkowania do tego uporządkowania za pomocą XdUQk. djest drzewok jest permutacją.

Następnie rozdzielamy krawędzie na osobne listy za pomocą c ... 2, sortujemy wierzchołki w obrębie każdej krawędzi za pomocą SM, sortujemy krawędzie w drzewie za pomocą S, dając kanoniczną reprezentację każdego drzewa. Te dwa kroki to kod mSSMcXdUQk2.pQ.

Teraz mamy listę list składającą się z każdego możliwego znakowania każdego drzewa. Sortujemy te listy za pomocą S. Dowolne dwa drzewa izomorficzne muszą mieć możliwość ponownego znakowania w grupie drzew. Korzystając z tego faktu, konwertujemy każdą listę na ciąg znaków za pomocą `, a następnie tworzymy zestaw tych list za pomocą {i wypisujemy jego długość za pomocą l.

isaacg
źródło