Розріжте золотий ланцюжок


32

Подорожньому потрібно зупинитися на п’ять днів у готелі за містом. У нього немає готівки, а термін його дії закінчується. Але він має золотий ланцюжок з російськими ланками.

Правило в цьому готелі полягає в тому, що мешканці повинні платити оренду кожного ранку. Мандрівник домовляється з менеджером про сплату однієї ланки золотого ланцюжка за кожен день. Але менеджер також вимагає, щоб подорожуючий щонайменше завдав шкоди ланцюжку, платячи щодня. Іншими словами, він повинен придумати рішення, щоб перерізати якомога менше посилань.

Вирізання посилання створює три підрозділи: одна, що містить лише зрізане посилання, і одна з кожної сторони. Наприклад, розрізання третьої ланки ланцюга довжиною 8 створює підсхеми довжиною [2, 1, 5]. Керівник із задоволенням вносить зміни, тому мандрівник може оплатити перший день ланцюжком довжиною 1, потім другий день ланцюжком довжиною 2, повернувши перший ланцюг назад.

Ваш код повинен вводити довжину n та виводити список посилань на скорочення мінімальної довжини.

Правила :

  • n - ціле число> 0.
  • Для посилань можна використовувати індексацію на основі 0 або 1.
  • Для деяких чисел рішення не є унікальним. Наприклад, якщо n = 15обидва [3, 8]і [4, 8]є дійсними результатами.
  • Ви можете повернути список або надрукувати його будь-яким розумним роздільником.
  • Це , тому виграє найкоротший код у байтах.

Тестові приклади :

Input          Output (1-indexed)
1              []
3              [1]
7              [3]
15             [3, 8]
149            [6, 17, 38, 79]

Детальний приклад

При n = 15 розрізання ланок 3 і 8 призводить до підрядів довжини [2, 1, 4, 1, 7]. Це правильне рішення, оскільки:

 1 = 1
 2 = 2
 3 = 1+2
 4 = 4
 5 = 1+4
 6 = 2+4
 7 = 7
 8 = 1+7
 9 = 2+7
10 = 1+2+7
11 = 4+7
12 = 1+4+7
13 = 2+4+7
14 = 1+2+4+7
15 = 1+1+2+4+7

Не існує рішення лише з одним розрізом, тому це оптимальне рішення.

Додаток

Зауважте, що ця проблема пов'язана з цілим розділенням. Ми шукаємо розбиття P з п , що всі цілі числа від 1 до п , по крайней мере , один patition , який є підмножиною P .

Ось відео YouTube про один можливий алгоритм цієї проблеми.


Я не розумію вашої посилання на "внесення змін". У вашому розміщеному прикладі на другий день ви платите за допомогою 2-ланкової ланцюга (і отримуєте 1-ланцюгову ланцюжок (яку ви заплатили за день раніше) як зміну відповідно до вашого пояснення). Але на третій день ви платите 1+2. Звідки взявся другий ланцюг 2-ланки?
Flater

4
@Flater У менеджера вже є. Ми просто сплачуємо додатковий. Насправді RHS - це посилання, якими володіє менеджер щодня
polfosol ఠ_ఠ

Відповіді:


15

05AB1E , 23 11 8 байт

ΔÍN-;иg=

Спробуйте в Інтернеті!

Використовує індексацію на основі 0.

Пояснення:

             # start from the implicit input
Δ            # loop forever
 Í           # subtract 2
  N-         # subtract the current iteration number
    ;        # divide by 2
     и       # create a list of length x
      g      # get the length of the list
       =     # print

иgсхоже на noop, але він насправді робить дві корисні речі: він скорочується до цілого числа ( ;повертає поплавок), і він руйнує інтерпретатор, якщо x негативний (це єдина умова виходу).


Рішення на 23 байти використовувало зовсім інший підхід, тому тут це для нащадків: ÅœʒR2äθP}ʒæOê¥P}θ2äθη€O( TIO , пояснення ).


2
Я видалив свою відповідь. Моє буття 42, а ваше 11 років - це занадто велика різниця для мене, щоб я не зніяковів, ха-ха. ;) Хороша відповідь, хоча, і хай на Ø.Ø. Ви просто спробували кілька випадкових речей для того, щоб обидва етапи відобразили і відобразили всі мінуси -1? Незалежно, дуже приємна відповідь і набагато коротша, ніж я передбачав. Я думав приблизно на 20 байт після того, як я розмістив свій поганий 42-байт.
Кевін Кройсейсен

2
@KevinCruijssen Nnope, Ø.Øнасправді була моєю першою ідеєю. Ваш коментар надихнув мене спробувати випадкові речі: я знайшов ®Ÿà, ï®Mі що більш важливо, иg, що дає це хороший 8-byter. Мені завжди було прикро, що osabie воліє нічого не робити над збоями у багатьох випадках (div на 0, неправильний тип тощо), тому ця аварія стане в нагоді.
Гримі

2
Hehe, 05AB1E, як вважається, ніколи не вийде з ладу, але ти маєш рацію, що іноді це трохи дратує, це ніколи не відбувається. У спадщині я навіть не знав, як врізатися, і в минулому ми навіть мали чіткий поділ на нульову помилку ми могли викликати вручну. xD У новій версії він все ще виходить із помилкою досить часто, коли дає неправильні аргументи певним вбудованим. І приємно знову -3.
Кевін Кройсейсен

2
"вибиває інтерпретатор, якщо x негативний (це єдина умова виходу)." - Мені це подобається
Джон Дворак

9

Python 2 , 75 байт

f=lambda n,i=0:n>=i<<i and f(n,i+1)or[min(n,2**j*i-i+j)for j in range(1,i)]

Спробуйте в Інтернеті!


Пояснення:

Побудовує послідовність "двійкових" фрагментів з базовим номером, що відповідає кількості скорочень.

Наприклад:

63 можна зробити в 3 розрізи, що означає перегородку в базі-4 (оскільки у нас є 3 одиничні кільця):

Вирізи:, 5, 14, 31що дає ланцюги 4 1 8 1 16 1 32(сортується:) 1 1 1 4 8 16 32.

Всі номери можна зробити:

1       1
2       1 1
3       1 1 1
4       4
...
42      1 1 8 32
...
62      1 1 4 8 16 32
63      1 1 1 4 8 16 32

Інші приклади:

18: 4,11        ->  3 1 6 1 7
27: 5,14,27     ->  4 1 8 1 13 1
36: 5,14,31     ->  4 1 8 1 16 1 5
86: 6,17,38,79  ->  5 1 10 1 20 1 40 1 7

1
Ви не повинні додати f=до початку? Оскільки ви використовуєте виклик fу функції лямбда, і я можу лише припустити, що ви посилаєтесь на ту саму лямбда, яку ви визначаєте.
randomdude999

@ randomdude999, Так, я забув ...
TFeld

@ randomdude999 це правило стосується всіх мов чи просто python? Тому що я бачу відповідь javascript, яка є чистою лямбдаю в цьому виклику ...
Тінь

3
@Shadow Застосовується для всіх мов, але лише для рекурсивних лямбдів.
TFeld

1
@Shadow Більш загальним правилом є те, що ви не можете посилатись на те, що не визначено ні у вашому коді, ні глобально визначено вашою мовою, якщо це прямо не дозволено викликом. Найпоширеніший випадок - рекурсивна функція, але це стосується інших ситуацій. Ця відповідь є ще одним прикладом: fне є рекурсивним, але він, однак, посилається в коді, і тому його потрібно назвати.
Арнольд

8

R , 77 69 байт

-8 байт завдяки Аарону Хейману

pmin(n<-scan(),0:(k=sum((a=2:n)*2^a<=n))+cumsum((k+2)*2^(0:k))+1)[-n]

Спробуйте в Інтернеті!

кк(к+1)2кн1,1,,1к(к+1),2(к+1),4(к+1),8(к+1),,(к+1)2к-1

(Можливо, останній підряд буде скорочений, якщо ми перевищимо загальну довжину ланцюга.)

Ungolfed (на основі попередньої, подібної версії):

n = scan()                            # read input
if(n - 1){                            # If n==1, return NULL
  k = match(F, (a = 2:n) * 2 ^ a > n) # compute k
  b = (k + 1) * 2 ^ (1:k - 1)         # lengths of subchains
  c = 1:k + cumsum(b)                 # positions of cuts
  pmin(c, n )                         # if last value is >n, coerce it to n
}

кккк+12к+12к+24к+4к(к+1)2к-1.)

а(к)нка(к)


Я сумніваюся, що це допоможе в окремому випадку n=1, але альтернативним способом генерування скорочень є повторення 1, 4, 4a(n-1)-4a(n-2).
Пітер Тейлор

@PeterTaylor У мене був аналогічний цикл обчислень k; це відповідає OEIS A134401: oeis.org/A134401 Але моя реалізація відношення повторення займає більше байтів, ніж поточний код.
Робін Райдер

Трохи перестановки я знизив до 73. Спробуйте в Інтернеті!
Аарон Гейман

@AaronHayman Дякую! Розумний хід, використовуючи sumзамість match.
Робін Райдер

69 байт і позбувся цього, якщо заява, яка вас засмутила: Спробуйте в Інтернеті!
Аарон Гейман



2

C ++, 109 , 107 байт

-2 байти завдяки Кевіну

#include<iostream>
main(){int n,k=0;for(std::cin>>n;++k<<k<n;);for(n-=k;n>0;k*=2,n-=k+1)std::cout<<n<<',';}

Алгоритм схожий на відповідь Робіна Райдера. Код написаний у складеному цілому вигляді. Спробуй це!

Деталі:

std::cin>>n;               // get the value of n as input
while(++k<<k<n);           // determine k
for(n-=k;n>0;k*=2,n-=k+1)  // we don't need n, so the lengths...
    std::cout<<n<<' ';     // of links are subtracted repeatedly

Це зміна С з однаковою довжиною байтів (не здається, що потрібна окрема відповідь):

#include<stdio.h>
main(){int n,k=0;for(scanf("%d",&n);++k<<k<n;);for(n-=k;n>0;k*=2,n-=k+1)printf("%d,",n);}

Дві незначні речі для гольфу: =0після kїх можна видалити, оскільки його 0за замовчуванням. std::cin>>n;while(++k<<k<n);може бути for(std::cin>>n;++k<<k<n;);. У мене також є відчуття, що for(n-=k;n>0;k*=2,n-=k+1)можна якось спростити комбінуючи речі, але не знаю як. PS: Змінення розділового кома на пробіл виглядає дещо краще, оскільки ви не бачите
зворотного

1
@KevinCruijssen Спасибі, але деякі компілятори не присвоюють нестатичним змінним значення за замовчуванням. Тому я подумав, що =0це потрібно для портативності;) Я також зрозумів, що простір після #includeне потрібен.
polfosol ఠ_ఠ

Ну, тоді добре. Я не знаю надто добре C ++, тому я використав цей онлайн-компілятор, який ви пов’язали у своїй відповіді, щоб перевірити деякі речі. :) Ви забули другу зміну, яку я запропонував у своєму коментарі: цикл while-циклу для циклу for і введення його std::cin>>nвсередину.
Кевін Кройсейсен


1

Сітківка 0,8,2 , 61 байт

.+
11,$&$*
+`\b(1+),(\1(1*)1?\3)$
1$2¶1$1,$3
1+,
1
1A`
1+
$.&

Спробуйте в Інтернеті! 1-індексований порт відповіді @ Grimy. Пояснення:

.+
11,$&$*

Почніть з, N=2а вхід перетворений на одинаковий.

+`\b(1+),(\1(1*)1?\3)$

Неодноразово намагайтеся відняти Nвід введення, а потім розділіть на 2.

1$2¶1$1,$3

У разі успіху запам'ятайте на 1 більше, ніж вхід у попередньому рядку, збільшення Nна поточному рядку та оновіть введення на нове значення.

1+,
1

Видаліть Nі збільшуйте останнє значення, щоб воно також було 1-індексованим.

1A`

Видаліть збільшений вихідний вхід.

1+
$.&

Перетворіть результати в десяткові.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.