Врятуйте пілота!


12

Питання

Нас захоплює армія роботів на їх космічній станції. Наш пілот космічного корабля знаходиться у в'язниці, яка знаходиться на рівні 1. Є лише один спосіб, як врятуватися, і він рятує вашого пілота космічного корабля. Це означає перехід від рівня N до рівня 1. Однак, оскільки це дуже ризиковано, вам потрібно потрапити до в'язниці якомога менше кроків.

Умови

  • Існує 4 способи переміщення:

    1. Перехід від рівня N до рівня N - 1 e.g. from 12 to 11
    2. Перехід від рівня N до рівня N + 1 e.g. from 12 to 13
    3. Використовуйте телепорт від рівня 2k до рівня k e.g. from 12 to 6
    4. Використовуйте телепорт від рівня 3k до рівня k e.g. from 12 to 4
  • Телепорти - лише в одну сторону (можна отримати від 12 до 4, але неможливо дістатись від 4 до 12)

  • Кожна дія займає один крок

Вхідні дані

Введення даних слід читати з STDIN або найближчої альтернативи вашій мові програмування. Введення складається з цілого числа nде 1 <= n <= 10^8.

Вихідні дані

Вихід повинен бути мінімальною кількістю кроків, які потрібно пройти з nрівня 1.

Приклади

Level         Minimum number of steps
1             0
8             3
10            3
267           7
100,000,000   25

Спробуйте зашифрувати програму, яка допоможе нам в найкоротші терміни врятувати наш пілот космічного корабля з в'язниці та повернутися додому!

Найкоротший код виграє!


7
Доцільно (але не обов’язково) почекати хоча б тиждень, перш ніж приймати відповідь. Навіть якщо ви маєте намір змінити прийняту відповідь, якщо в майбутньому буде розміщена коротша, у інших може скластись враження, що цей конкурс закінчився і утриматися від участі.
Денніс

1
Цей виклик нагадує мені гру, в яку я грала за допомогою свого калькулятора: я б набрала число, яке заповнює екран, і спробую ділити на 2, 3 або 5 на скільки я можу, а потім додавати / віднімати 1 і продовжувати.
Арктур

Відповіді:


8

Pyth, 32 байти

L?<b3tbhSsmm+h%_Wkbdy/+kbd2tS3yQ

Спробуйте в Інтернеті: Демонстрація або Тестовий набір

Пояснення:

Я трохи перетворив проблему. Я визначаю 4 нові операції, які замінюють 4 операції питання.

  • level / 2(вважається (level % 2) + 1кроками, тому що, можливо, вам потрібно буде спочатку перемістити рівень вниз, щоб телепортувати)
  • (level + 1) / 2(вважається (level % 2) + 1кроками)
  • level / 3(вважається (level % 3) + 1кроками)
  • (level + 1) / 3(вважається (-level % 3) + 1кроками)

За своєю конструкцією ці операції можуть бути застосовані до кожного номеру, якщо номер 0 mod 2, 1 mod 2, 0 mod 3, 1 mod 3або 2 mod 3.

Ви можете легко подумати, чому це працює. Основна ідея полягає в тому, що існує принаймні одне оптимальне рішення, яке не має двох (рух вниз) або двох (рух вгору) рухів підряд. Доказ: Якщо у вас є рішення, у якому є два такі рухи підряд, ви можете замінити їх і зробити розчин меншим або рівним по довжині. Наприклад, ви можете замінити (переміщення вгору, переміщення вгору, телепорт від 2k до k) на (teleport від 2k до k, переміщення вгору) і подібне у всіх інших випадках.

L?<b3tbhSsmm+h%_Wkbdy/+kbd2tS3yQ
L                                 define a function y(b), which returns:
 ?<b3                               if b < 3:
     tb                               return b-1
                                    else:
          m                tS3        map each d in [2, 3] to:
           m              2             map each k in [0, 1] to:
              %_Wkbd                      (b mod d) if k == 0 else (-b mod d)
             h                            +1, this gives the additional steps
            +       y/+kbd                + f((b+k)/d) (recursive call)
         s                            combine the two lists
       hS                             and return the minimum element
                               yQ call y with input number

Функція yвикористовує неявну мемоалізацію, і тому час виконання не вибухає.


1
Основна думка полягає в тому, що в оптимальному рішенні ніколи не є два (рух вниз) або два (рух вгору) руху. - що з 29 -> 28 -> 27 -> 9 -> 3 -> 1? Якщо це оптимальне рішення, то як ви вирішили, що завжди існує альтернатива шляху "вгору / вниз", який не призводить до більш незграбної області чисел?
TessellatingHeckler

1
@TessellatingHeckler Можливо, я мав би бути більш точним. Існує хоча б одне оптимальне рішення, яке не має двох (рух вниз) або двох (рух вгору) рухів підряд. Напр. 29 -> 30 -> 10 -> 9 -> 3 -> 1
Якубе

Я не кажу, що це неправильно, тільки що я не можу «легко подумати, чому це працює». Я міркував: найшвидший шлях до кімнати 1 починається з потужності три, ділити на три кожен крок. Таким чином, найшвидший маршрут для чисел, що мають потужність три, - це повторне віднімання або додавання, щоб дістати до сили трьох, а потім ділити повторно на 3. Якщо замість цього вони починаються з переміщення n / 2, вони дістаються далі від сили трьох, і тому минув найшвидший можливий маршрут. Я не бачу, як вони / завжди / знайдуть інший маршрут настільки ж короткий; здається, зараз вони перебувають у регіоні з гіршим вибором.
TessellatingHeckler

4

Пітон, 176 байт

n=int(1e8);s,f,L=0,input(),[1,0]+[n]*(n-1)
def h(q):
 if 0<=q<=n and L[q]>s:L[q]=s+1
while n in L:
 for i,v in enumerate(L):
  if v==s:map(h,(i-1,i+1,i*2,i*3))
 s+=1
print L[f]

Жорстока сила на всьому шляху; список усіх чисел 1 to 100,000,000на 64-бітовому комп'ютері становить ~ 800 Мб пам'яті.

Індекс списку представляє числа, значення представляють відстань від 1 у дозволених кроках порятунку.

  • Встановити список [1] ​​= 0, що означає "доступний за 0 кроків".
  • для кожного номера у списку, який можна отримати за 0 кроків (тобто 1)
    • встановити число + 1, число-1, число * 2, число * 3, доступне за 1 крок
  • для кожного номера у списку, який доступний за 1 крок (тобто 0,2,2,3)
    • встановити число + 1, число-1, число * 2, число * 3, доступне за 2 кроки
  • ... тощо, поки не буде досягнуто кожного індексу списку.

Час виконання трохи більше 10 хвилин. * ах *.

Зауваження до коду

n=int(1e8)           # max input limit.
s=0                  # tracks moves from 1 to a given number.
f=input()            # user input.

L=[1,0]+[n]*(n-1)    # A list where 0 can get to room 1 in 1 step,
                     # 1 can get to itself in 0 steps,
                     # all other rooms can get to room 1 in 
                     # max-input-limit steps as a huge upper bound.


def helper(q):
    if 0<=q<=n:      # Don't exceed the list boundaries.
      if L[q]>s:     # If we've already got to this room in fewer steps
                     # don't update it with a longer path.
          L[q]=s+1   # Can get between rooms 1 and q in steps+1 actions.


while n in L:        # until there are no values still at the 
                     # original huge upper bound

 for i,v in enumerate(L):
  if v==s:                            # only pick out list items
                                      # rechable in current s steps,
      map(helper,(i-1,i+1,i*2,i*3))   # and set the next ones reachable
                                      # in s+1 steps.

 s+=1                                 # Go through the list again to find
                                      # rooms reachable in s+1 steps

print L[f]                            # show answer to the user.

Інший

  • Якщо ви запускаєте його в PythonWin, ви можете отримати доступ до списку L в інтерпретаторі.
  • У кожній кімнаті є шлях до капітана в 30 ходів або менше.
  • Тільки одна кімната знаходиться в 30 ходах - кімната 72 559 411 - і є 244 номери, які за 29 кроків.
  • Він може мати жахливі характеристики виконання для максимального випадку, але одне з коментарів до питання " @Geobits всі програми, які повинні знайти найкоротші шляхи для 20000 тестових випадків за 5 хвилин ", і він тестує 1-20,001 за <6 секунд.

2

Пітон 2 ... 1050

погано написана, невольфська, неоптимальна.

Читає початковий рівень на stdin, друкує мінімальну кількість кроків на stdout.

def neighbors(l):
    yield l+1
    yield l-1
    if not l%3:yield l/3
    if not l%2:yield l/2

def findpath(start, goal):
    closedset = set([])
    openset = set([start])
    came_from = {}
    g_score = {}
    g_score[start] = 0
    f_score = {}
    f_score[start] = 1
    while len(openset) > 0:
        current = min(f_score, key=f_score.get)
        if current == goal:
            return came_from
        else:
            openset.discard(current)
        del f_score[current]
        closedset.add(current)
        for neighbor in neighbors(current):
            if neighbor in closedset:
                continue
            tentative_g_score = g_score[current] + 1
            if (neighbor not in openset) or (tentative_g_score < g_score[neighbor]):
                came_from[neighbor] = current
                g_score[neighbor] = tentative_g_score
                f_score[neighbor] = tentative_g_score + 1
                openset.add(neighbor)
def min_path(n):
    path = findpath(n,1)
    i,current = 0,1
    while current <> n:
        i+=1
        current = path[current]
    return i
print min_path(input())

2

32-розрядний машинний код x86, 59 байт

У шістнадцятковій формі:

31c9487435405031d231dbb103f7f14a7c070f94c301d008d353e8e3ffffff5b00c3585331d2b102f7f152e8d2ffffff5a00d05a38d076019240c3

Мова машини сама по собі не має поняття стандартного введення. Оскільки виклик чистий обчислювальний, я вирішив написати функцію, що приймає вхідний параметр EAXі повертає результат у AL.

Математика за кодом добре пояснюється @Jakube: пошук ведеться лише серед шляхів, в яких телепорти перемежовуються не більше ніж одним однорівневим ходом. Продуктивність складає близько 12000 тестових випадків в секунду в нижньому кінці діапазону введення і 50 випадків в секунду у верхньому. Споживання пам'яті становить 12 байт простору стеку на рівень рекурсії.

0:  31 c9               xor ecx, ecx  
_proc:
2:  48                  dec eax       
3:  74 35               je _ret       ;if (EAX==1) return 0;
5:  40                  inc eax       ;Restore EAX
6:  50                  push eax      
7:  31 d2               xor edx, edx  ;Prepare EDX for 'div'
9:  31 db               xor ebx, ebx  
b:  b1 03               mov cl, 3     
d:  f7 f1               div ecx       ;EAX=int(EAX/3); EDX=EAX%3
f:  4a                  dec edx       ;EDX is [-1..1]
10: 7c 07               jl _div3      ;EDX<0 (i.e. EAX%3==0)
12: 0f 94 c3            sete bl       ;BL=EDX==0?1:0
15: 01 d0               add eax, edx  ;If EAX%3==2, we'd go +1 level before teleport
17: 08 d3               or bl, dl     ;BL=EAX%3!=0
_div3:
19: 53                  push ebx      ;Save register before...
1a: e8 e3 ff ff ff      call _proc    ;...recursive call
1f: 5b                  pop ebx       
20: 00 c3               add bl, al    ;BL is now # of steps if taken 3n->n route (adjusted) less one
22: 58                  pop eax       ;Restore original input parameter's value
23: 53                  push ebx      
24: 31 d2               xor edx, edx  
26: b1 02               mov cl, 2     
28: f7 f1               div ecx       ;EAX=EAX>>1; EDX=EAX%2
2a: 52                  push edx      ;Save register before...
2b: e8 d2 ff ff ff      call _proc    ;...another recursive call
30: 5a                  pop edx       
31: 00 d0               add al, dl    ;AL is now # of steps if using 2n->n route (adjusted) less one
33: 5a                  pop edx       
34: 38 d0               cmp al, dl    ;Compare two routes
36: 76 01               jbe _nsw      
38: 92                  xchg eax, edx ;EAX=min(EAX,EDX)
_nsw:
39: 40                  inc eax       ;Add one for the teleport itself
_ret:
3a: c3                  ret           
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.