Олимпийские виноградники выполняют свои упражнения на стандартных деревьях. В частности, Стандартное дерево n
имеет вершины для 0
сквозного прохождения n-1
и ребра, связывающие каждую ненулевую вершину a
с вершиной n % a
под ней. Так, например, Standard Tree 5 выглядит так:
3
|
2 4
\ /
1
|
0
потому что остаток, когда 5 делится на 3, равен 2, остаток, когда 5 делится на 2 или 4, равен 1, а остаток, когда 5 делится на 1, равен 0.
В этом году Тарзан будет защищать свое золото с помощью новых процедур, каждая из которых начинается в вершине n - 1
, качается в вершину n - 2
, продолжается в вершину n - 3
и т. Д., Пока, наконец, он не спешится в вершину 0
.
Балл за рутину - это сумма баллов за каждый свинг (включая демонтаж), а за свинг - это расстояние в дереве между его начальной и конечной точками. Таким образом, процедура Тарзана на Стандартном Дереве 5 имеет оценку 6:
- размах от
4
до3
трех очков (вниз, вверх, вверх), - свинг от
3
до2
одно очка (вниз), - свинг от
2
до1
десятков в одной точке (вниз), и - отсоединились от
1
до0
одно очка (вниз).
Напишите программу или функцию, которая, учитывая положительное целое число n
, вычисляет оценку рутины Тарзана на Стандартном дереве n
. Образцы входов и выходов:
1 -> 0
2 -> 1
3 -> 2
4 -> 6
5 -> 6
6 -> 12
7 -> 12
8 -> 18
9 -> 22
10 -> 32
11 -> 24
12 -> 34
13 -> 34
14 -> 36
15 -> 44
16 -> 58
17 -> 50
18 -> 64
19 -> 60
20 -> 66
21 -> 78
22 -> 88
23 -> 68
24 -> 82
Правила и оценка кода как обычно для код-гольфа .
источник
Ответы:
C
9897 байтЭто вычисляет расстояние между каждой парой точек по следующей формуле:
Это имеет то преимущество, что применительно ко всем парам это то же самое, что и:
Чтобы упростить логику, мы предполагаем, что мы идем от узла 0 к узлу n-1, а не от n-1 до 0, как указано в вопросе. Расстояние от корневого узла до узла 0, очевидно, равно 0 (они одинаковые). И мы можем видеть, что для (большинства) деревьев расстояние от последнего узла до корня равно 2:
Это означает, что у нас есть некоторые особые случаи (n <2), но мы можем объяснить их достаточно легко.
Сломать:
Спасибо @feersum за 1 байт сохранен
Бонус: деревья!
Я написал программу быстрого и грязного, чтобы увидеть, как эти деревья выглядят. Вот некоторые из результатов:
19:
источник
Python 2, 85 байт
источник
Perl,
65595554 байтаВключает +2 для
-ap
Запустите с размером дерева на STDIN:
vines.pl
:объяснение
Если переписать дерево
где каждый узел содержит множество всех своих предков и самого себя:
Тогда мы можем описать, например, все узлы пути от 4 до 3 как:
Количество ребер на единицу меньше количества узлов, поэтому мы можем использовать это для игнорирования точки соединения, поэтому число ребер на пути от 4 до 3 равно 3, потому что:
Обратите внимание, что это также работает для пути, который непосредственно идет к его цели, например, для пути от 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 является предком. Я оставлю в конце места. Итак, фактическое представление дерева, которое я построю, таково:Обратите внимание, что я пропустил узел 0, который будет представлен,
"11111"
потому что я собираюсь игнорировать узел 0 (он никогда не вносит свой вклад).Предки без нижнего соседа теперь представлены концом последовательности 1. Предки без более высокого соседа теперь представлены началом последовательности 1, но мы должны игнорировать любое начало последовательности в начале строки, так как это будет представлять путь,
5 to 4
который не существует. Эта комбинация точно соответствует регулярному выражению/.\b/
.Построение строк предков выполняется путем обработки всех узлов в указанном порядке
n-1 .. 1
и установки 1 в позицию для самого узла и выполнения «или» в потомке.При этом программа достаточно проста для понимания:
Обратите внимание, что замена
/.\b/
на/\b/
решает версию этой проблемы в обе стороны, где Тарзан также берет путь0 to n-1
Некоторые примеры того, как выглядят строки предков (приведены по порядку
n-1 .. 1
):источник
Mathematica,
113103102 байта-10 байт благодаря @feersum; -1 байт благодаря @MartinEnder
Следующее выполняется намного быстрее (но, к сожалению, длиннее - 158 байт ):
источник
With
. Также, похоже, каждый раз, когдаRange
используетсяa
аргумент, это может быть учтено.r=Range[a=#-1]
сохраняет байт.J 37 байт
Использование:
Попробуйте это онлайн здесь.
источник
JavaScript (ES6),
118116 байтОтсутствие функции установки разницы действительно вредит, но некоторая креативная рекурсия немного уменьшает количество байтов. Редактировать: Сохранено 2 байта путем удаления ненужного параметра.
источник