Perl, 65 59 55 54 bytes
Inclui +2 para -ap
Execute com o tamanho da árvore no STDIN:
for i in `seq 24`; do echo -n "$i: "; vines.pl <<< $i; echo; done
vines.pl
:
#!/usr/bin/perl -ap
$_=map{${"-@F"%$_}|=$_=$$_|$"x$p++.1;/.\b/g}1-$_..-1
Explicação
Se você reescrever a árvore
3
|
2 4
\ /
1
|
0
para aquele em que cada nó contém o conjunto de todos os seus ancestrais e ele próprio:
{3}
|
{2,3} {4}
\ /
\ /
{1,2,3,4}
|
{0,1,2,3,4}
Então, podemos descrever, por exemplo, todos os nós do caminho de 4 a 3 como:
- Todos os nós que contêm 3, mas não 4 (saindo de 3)
- Todos os nós que contêm 4, mas não 3 (saindo de 4)
- O nó mais alto que contém 3 e 4 (a junção)
O número de arestas é um a menos que o número de nós; portanto, podemos usá-lo para ignorar o ponto de junção; portanto, o número de arestas no caminho de 4 a 3 é 3 porque:
- O número de nós que contêm 3, mas não nós 4: 2
- O número de nós que contêm 4, mas não o nó 3: 1
Observe que isso também funciona para um caminho que desce diretamente para o seu destino, por exemplo, para o caminho de 3 a 2, o número de arestas é 1 porque:
- O número de nós que contêm 2, mas não 3: 0 nós
- O número de nós que contêm 3, mas não o nó 2: 1
Podemos então somar todas essas combinações.
Se você olhar apenas para um nó, por exemplo, nó 2 com conjunto de ancestrais {2,3}
. Este nó contribuirá uma vez ao processar o caminho 2 to 1
porque contém um 2, mas não um 1. Ele não contribuirá com nada para o caminho, 3 to 2
pois possui 2 e 3, mas contribuirá uma vez ao processar o caminho . O Simular 0 é unilateral, mas como 0 está sempre na raiz da árvore e contém todos os números (é a junção final e não contamos junções), nunca há contribuição de 0, portanto é mais fácil deixar o nó 0 completamente.4 to 3
pois possui 3, mas não 4. Em geral, um número no conjunto ancestral de um nó contribuirá com um para cada vizinho (um menor ou mais alto) que não esteja no conjunto. Exceto pelo elemento máximo (4 neste caso), que apenas contribui para o vizinho baixo 3, pois não há caminho5 to 4
Portanto, também podemos resolver o problema observando o conjunto de ancestrais de cada nó, contar as contribuições e somar todos os nós.
Para processar vizinhos facilmente, vou representar os conjuntos de ancestrais como uma sequência de espaços e 1's, onde cada 1 na posição p representa que n-1-p é um ancestral. Então, por exemplo, no nosso caso de n=5
1 na posição 0, indica 4 é um ancestral. Vou deixar espaços à direita. Portanto, a representação real da árvore que construirei é:
" 1"
|
" 11" "1"
\ /
\ /
"1111"
Observe que eu deixei de fora o nó 0, que seria representado por, "11111"
porque eu vou ignorar o nó 0 (ele nunca contribui).
Antepassados sem vizinho inferior agora são representados no final de uma sequência de 1s. Antepassados sem vizinho mais alto agora são representados pelo início de uma sequência de 1s, mas devemos ignorar qualquer início de uma sequência no início de uma sequência, pois isso representaria o caminho 5 to 4
que não existe. Essa combinação é exatamente correspondida pelo regex /.\b/
.
A construção das seqüências ancestrais é feita processando todos os nós na ordem n-1 .. 1
e, em seguida, defina 1 na posição do próprio nó e fazendo um "ou" no descendente.
Com tudo isso, o programa é fácil de entender:
-ap read STDIN into $_ and @F
map{ }1-$_..-1 Process from n-1 to 1,
but use the negative
values so we can use a
perl sequence.
I will keep the current
ancestor for node $i in
global ${-$i} (another
reason to use negative
values since $1, $2 etc.
are read-only
$$_|$"x$p++.1 "Or" the current node
position into its ancestor
accumulator
$_= Assign the ancestor string
to $_. This will overwrite
the current counter value
but that has no influence
on the following counter
values
${"-@F"%$_}|= Merge the current node
ancestor string into the
successor
Notice that because this
is an |= the index
calculation was done
before the assignment
to $_ so $_ is still -i.
-n % -i = - (n % i), so
this is indeed the proper
index
/.\b/g As explained above this
gives the list of missing
higher and lower neighbours
but skips the start
$_= A map in scalar context
counts the number of
elements, so this assigns
the grand total to $_.
The -p implicitly prints
Observe que a substituição /.\b/
por /\b/
resolve a versão de ida e volta desse problema em que o tarzan também segue o caminho0 to n-1
Alguns exemplos de como as seqüências de caracteres ancestrais são exibidas (em ordem n-1 .. 1
):
n=23:
1
1
1
1
1
1
1
1
1
1
1
11
1 1
1 1
1 1
11 11
1 1
11 1 1 11
1 1
1111 11 11 1111
111111111 111111111
1111111111111111111111
edges=68
n=24:
1
1
1
1
1
1
1
1
1
1
1
1
1 1
1 1
1 1
1 1
1 1
1 1 1 1
1 1
11 1 1 11
1 1 1 1
1 1 1 1
1 1
edges=82