Perl, 65 59 55 54 bytes
Incluye +2 para -ap
Ejecutar con el tamaño del árbol en 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
Explicación
Si reescribes el árbol
3
|
2 4
\ /
1
|
0
a uno donde cada nodo contiene el conjunto de todos sus antepasados y a sí mismo:
{3}
|
{2,3} {4}
\ /
\ /
{1,2,3,4}
|
{0,1,2,3,4}
Luego podemos describir, por ejemplo, todos los nodos, la ruta de 4 a 3 como:
- Todos los nodos que contienen 3 pero no 4 (bajando de 3)
- Todos los nodos que contienen 4 pero no 3 (bajando de 4)
- El nodo más alto que contiene 3 y 4 (la unión)
El número de bordes es uno menos que el número de nodos, por lo que podemos usarlo para ignorar el punto de unión, por lo que el número de bordes en la ruta de 4 a 3 es 3 porque:
- El número de nodos que contienen 3 pero no 4: 2 nodos
- El número de nodos que contienen 4 pero no 3: 1 nodo
Tenga en cuenta que esto también funciona para una ruta que desciende directamente a su objetivo, por ejemplo, para la ruta de 3 a 2, el número de bordes es 1 porque:
- El número de nodos que contienen 2 pero no 3: 0 nodos
- El número de nodos que contienen 3 pero no 2: 1 nodo
Entonces podemos sumar todas estas combinaciones.
Si, en cambio, observa solo un nodo, por ejemplo, el nodo 2 con el conjunto ancestro {2,3}
. Este nodo contribuirá una vez cuando procese la ruta 2 to 1
porque contiene un 2 pero no un 1. No aportará nada para la ruta 3 to 2
ya que tiene 2 y 3, pero contribuirá una vez cuando procese la ruta 4 to 3
ya que tiene 3 pero no 4. En general, un número en el conjunto ancestro de un nodo contribuirá con uno para cada vecino (uno más bajo o más alto) que no esté en el conjunto. Excepto por el elemento máximo (4 en este caso) que solo contribuye para el vecino bajo 3 ya que no hay ruta5 to 4
. El 0 simular es unilateral, pero dado que 0 siempre está en la raíz del árbol y contiene todos los números (es la unión final y no contamos las uniones), nunca hay ninguna contribución de 0, por lo que es más fácil dejar el nodo 0 fuera por completo.
Por lo tanto, también podemos resolver el problema mirando el conjunto de antepasados para cada nodo, contar las contribuciones y sumar sobre todos los nodos.
Para procesar fácilmente a los vecinos, voy a representar los conjuntos de antepasados como una cadena de espacios y 1 donde cada 1 en la posición p representa que n-1-p es un antepasado. Entonces, por ejemplo, en nuestro caso de n=5
un 1 en la posición 0 indica que 4 es un antepasado. Dejaré los espacios finales. Entonces, la representación real del árbol que construiré es:
" 1"
|
" 11" "1"
\ /
\ /
"1111"
Observe que omití el nodo 0, que estaría representado por "11111"
porque voy a ignorar el nodo 0 (nunca contribuye).
Los antepasados sin vecino inferior ahora están representados por el final de una secuencia de 1. Los antepasados sin vecino superior ahora están representados por el inicio de una secuencia de 1, pero deberíamos ignorar cualquier inicio de una secuencia al comienzo de una cadena ya que esto representaría la ruta 5 to 4
que no existe. Esta combinación coincide exactamente con la expresión regular /.\b/
.
La construcción de las cadenas ancestrales se realiza procesando todos los nodos en el orden n-1 .. 1
y establece un 1 en la posición del nodo en sí y haciendo un "o" en el descendiente.
Con todo eso, el programa es lo suficientemente 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 reemplazar /.\b/
por /\b/
resuelve la versión de ida y vuelta de este problema donde tarzan también toma el camino0 to n-1
Algunos ejemplos de cómo se ven las cadenas ancestrales (dadas en orden 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