Perl, 65 59 55 54 байта
Включает +2 для -ap
Запустите с размером дерева на 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
объяснение
Если переписать дерево
3
|
2 4
\ /
1
|
0
где каждый узел содержит множество всех своих предков и самого себя:
{3}
|
{2,3} {4}
\ /
\ /
{1,2,3,4}
|
{0,1,2,3,4}
Тогда мы можем описать, например, все узлы пути от 4 до 3 как:
- Все узлы, которые содержат 3, но не 4 (понижаясь от 3)
- Все узлы, которые содержат 4, но не 3 (понижаясь с 4)
- Самый высокий узел, который содержит 3 и 4 (соединение)
Количество ребер на единицу меньше количества узлов, поэтому мы можем использовать это для игнорирования точки соединения, поэтому число ребер на пути от 4 до 3 равно 3, потому что:
- Количество узлов, которые содержат 3, но не 4: 2 узла
- Количество узлов, которые содержат 4, но не 3: 1 узел
Обратите внимание, что это также работает для пути, который непосредственно идет к его цели, например, для пути от 3 до 2 число ребер равно 1, потому что:
- Количество узлов, которые содержат 2, но не 3: 0 узла
- Количество узлов, которые содержат 3, но не 2: 1 узел
Затем мы можем суммировать по всем этим комбинациям.
Если вместо этого вы посмотрите только на узел, например, на узел 2 с установленным предком {2,3}
. Этот узел будет вносить один раз при обработке пути, 2 to 1
потому что он содержит 2, а не 1. Он ничего не даст для пути, 3 to 2
так как он имеет 2 и 3, но он будет вносить один раз при обработке пути, 4 to 3
поскольку у него есть 3, но нет 4. Как правило, число в наборе предков узла будет содержать одно для каждого соседа (один ниже или выше), которого нет в наборе. За исключением максимального элемента (в данном случае 4), который вносит вклад только для нижнего соседа 3, так как нет пути5 to 4
, Simular 0 является односторонним, но так как 0 всегда находится в корне дерева и содержит все числа (это конечное соединение, а мы не учитываем соединения), никогда не будет никакого вклада от 0, поэтому проще всего оставить узел 0 в целом.
Таким образом, мы также можем решить эту проблему, посмотрев набор предков для каждого узла, посчитав вклады и суммировав все узлы.
Чтобы легко обрабатывать соседей, я собираюсь представить наборы предков в виде строки пробелов и единиц, где каждый 1 в позиции p обозначает, что n-1-p является предком. Так, например, в нашем случае n=5
1 в позиции 0 означает, что 4 является предком. Я оставлю в конце места. Итак, фактическое представление дерева, которое я построю, таково:
" 1"
|
" 11" "1"
\ /
\ /
"1111"
Обратите внимание, что я пропустил узел 0, который будет представлен, "11111"
потому что я собираюсь игнорировать узел 0 (он никогда не вносит свой вклад).
Предки без нижнего соседа теперь представлены концом последовательности 1. Предки без более высокого соседа теперь представлены началом последовательности 1, но мы должны игнорировать любое начало последовательности в начале строки, так как это будет представлять путь, 5 to 4
который не существует. Эта комбинация точно соответствует регулярному выражению /.\b/
.
Построение строк предков выполняется путем обработки всех узлов в указанном порядке n-1 .. 1
и установки 1 в позицию для самого узла и выполнения «или» в потомке.
При этом программа достаточно проста для понимания:
-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
Обратите внимание, что замена /.\b/
на /\b/
решает версию этой проблемы в обе стороны, где Тарзан также берет путь0 to n-1
Некоторые примеры того, как выглядят строки предков (приведены по порядку 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