Perl, 65 59 55 54 octets
Comprend +2 pour -ap
Exécutez avec la taille de l'arbre sur 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
Explication
Si vous réécrivez l'arbre
3
|
2 4
\ /
1
|
0
à un où chaque nœud contient l'ensemble de tous ses ancêtres et lui-même:
{3}
|
{2,3} {4}
\ /
\ /
{1,2,3,4}
|
{0,1,2,3,4}
Ensuite, nous pouvons décrire par exemple tous les nœuds le chemin de 4 à 3 comme:
- Tous les nœuds qui contiennent 3 mais pas 4 (en descendant de 3)
- Tous les nœuds qui contiennent 4 mais pas 3 (en descendant de 4)
- Le nœud le plus élevé qui contient à la fois 3 et 4 (la jointure)
Le nombre d'arêtes est inférieur de un au nombre de nœuds afin que nous puissions l'utiliser pour ignorer le point de jointure, donc le nombre d'arêtes sur le chemin de 4 à 3 est 3 car:
- Le nombre de nœuds contenant 3 nœuds mais pas 4: 2
- Le nombre de nœuds contenant 4 nœuds mais pas 3: 1
Notez que cela fonctionne également pour un chemin qui descend directement vers sa cible, par exemple pour le chemin de 3 à 2, le nombre d'arêtes est 1 car:
- Le nombre de nœuds contenant 2 nœuds mais pas 3: 0
- Le nombre de nœuds contenant 3 nœuds mais pas 2: 1
On peut alors additionner toutes ces combinaisons.
Si vous regardez plutôt un nœud, par exemple le nœud 2 avec un ensemble d'ancêtres {2,3}
. Ce nœud va contribuer une fois lors du traitement du chemin 2 to 1
car il contient un 2 mais pas un 1. Il ne contribuera rien pour le chemin 3 to 2
car il a à la fois 2 et 3, mais il contribuera une fois lors du traitement du chemin 4 to 3
car il en a 3 mais non 4. En général, un nombre dans l'ensemble des ancêtres d'un nœud contribuera à un pour chaque voisin (un inférieur ou supérieur) qui n'est pas dans l'ensemble. Sauf pour l'élément maximum (4 dans ce cas) qui ne contribue que pour le voisin bas 3 car il n'y a pas de chemin5 to 4
. Le 0 simulaire est unilatéral, mais puisque 0 est toujours à la racine de l'arbre et contient tous les nombres (c'est la jointure ultime et nous ne comptons pas les jointures), il n'y a jamais de contribution de 0, il est donc plus facile de simplement quitter le nœud 0 tout à fait.
Nous pouvons donc également résoudre le problème en examinant l'ensemble d'ancêtres pour chaque nœud, compter les contributions et additionner tous les nœuds.
Pour traiter facilement les voisins, je vais représenter les ensembles d'ancêtres sous la forme d'une chaîne d'espaces et de 1 où chaque 1 à la position p représente que n-1-p est un ancêtre. Ainsi, par exemple, dans notre cas d' n=5
un 1 à la position 0, 4 indique un ancêtre. Je laisserai de côté les espaces de fuite. La représentation réelle de l'arbre que je vais construire est donc:
" 1"
|
" 11" "1"
\ /
\ /
"1111"
Notez que j'ai omis le nœud 0 qui serait représenté par "11111"
car je vais ignorer le nœud 0 (il ne contribue jamais).
Les ancêtres sans voisin inférieur sont maintenant représentés par la fin d'une séquence de 1. Les ancêtres sans voisin supérieur sont maintenant représentés par le début d'une séquence de 1, mais nous devons ignorer tout début d'une séquence au début d'une chaîne car cela représenterait le chemin 5 to 4
qui n'existe pas. Cette combinaison correspond exactement à l'expression régulière /.\b/
.
La construction des chaînes ancêtres se fait en traitant tous les nœuds dans l'ordre n-1 .. 1
et en définissant un 1 à la position du nœud lui-même et en faisant un "ou" dans le descendant.
Avec tout cela, le programme est assez facile à comprendre:
-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
Notez que le remplacement /.\b/
par /\b/
résout la version aller-retour de ce problème où tarzan prend également le chemin0 to n-1
Quelques exemples de l'apparence des chaînes ancêtres (données dans l'ordre 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