Perl, 65 59 55 54 byte
Include +2 per -ap
Esegui con la dimensione dell'albero su 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
Spiegazione
Se riscrivi l'albero
3
|
2 4
\ /
1
|
0
a uno in cui ciascun nodo contiene l'insieme di tutti i suoi antenati e se stesso:
{3}
|
{2,3} {4}
\ /
\ /
{1,2,3,4}
|
{0,1,2,3,4}
Quindi possiamo descrivere ad esempio tutti i nodi il percorso da 4 a 3 come:
- Tutti i nodi che contengono 3 ma non 4 (scendendo da 3)
- Tutti i nodi che contengono 4 ma non 3 (scendendo da 4)
- Il nodo più alto che contiene sia 3 che 4 (il join)
Il numero di bordi è uno in meno del numero di nodi, quindi possiamo usarlo per ignorare il punto di unione, quindi il numero di bordi sul percorso da 4 a 3 è 3 perché:
- Il numero di nodi che contengono 3 ma non 4: 2 nodi
- Il numero di nodi che contengono un nodo 4 ma non 3: 1
Si noti che questo funziona anche per un percorso che scende direttamente alla sua destinazione, ad esempio per il percorso da 3 a 2 il numero di bordi è 1 perché:
- Il numero di nodi che contengono 2 ma non 3: 0 nodi
- Il numero di nodi che contengono un nodo 3 ma non 2: 1
Possiamo quindi riassumere tutte queste combinazioni.
Se invece guardi solo un nodo, ad esempio nodo 2 con set di antenati {2,3}
. Questo nodo contribuirà una volta durante l'elaborazione del percorso 2 to 1
perché contiene un 2 ma non un 1. Non contribuirà nulla per il percorso 3 to 2
poiché ha sia 2 che 3, ma contribuirà una volta durante l'elaborazione del percorso 4 to 3
poiché ha 3 ma no 4. In generale un numero nell'insieme di antenati di un nodo contribuirà uno per ciascun vicino (uno più basso di più alto) che non è nell'insieme. Ad eccezione dell'elemento massimo (4 in questo caso) che contribuisce solo per il vicino basso 3 poiché non esiste alcun percorso5 to 4
. Lo 0 similare è unilaterale, ma poiché 0 è sempre alla radice dell'albero e contiene tutti i numeri (è il join finale e non contiamo i join) non c'è mai alcun contributo da 0, quindi è più facile lasciare il nodo 0 del tutto fuori.
Quindi possiamo anche risolvere il problema osservando il set di antenati per ciascun nodo, contando i contributi e sommando tutti i nodi.
Per elaborare facilmente i vicini, rappresenterò i set di antenati come una stringa di spazi e 1 dove ogni 1 nella posizione p rappresenta che n-1-p è un antenato. Quindi, ad esempio, nel nostro caso di n=5
un 1 nella posizione 0 indica che 4 è un antenato. Lascerò fuori gli spazi finali. Quindi la rappresentazione reale dell'albero che costruirò è:
" 1"
|
" 11" "1"
\ /
\ /
"1111"
Si noti che ho lasciato fuori il nodo 0 che sarebbe rappresentato dal "11111"
perché ignorerò il nodo 0 (non contribuisce mai).
Gli antenati senza vicini inferiori sono ora rappresentati dalla fine di una sequenza di 1. Gli antenati senza vicini più alti sono ora rappresentati dall'inizio di una sequenza di 1, ma dovremmo ignorare qualsiasi inizio di una sequenza all'inizio di una stringa poiché ciò rappresenterebbe il percorso 5 to 4
che non esiste. Questa combinazione è esattamente abbinata al regex /.\b/
.
La creazione delle stringhe dell'antenato viene eseguita elaborando tutti i nodi nell'ordine n-1 .. 1
e impostando un 1 nella posizione per il nodo stesso e facendo un "o" nel discendente.
Con tutto ciò che il programma è abbastanza facile da capire:
-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
Si noti che la sostituzione /.\b/
dal /\b/
risolve la versione andata e ritorno di questo problema dove tarzan prende anche il percorso0 to n-1
Alcuni esempi di come appaiono le stringhe degli antenati (fornite in ordine 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