Спаси пилота!


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) на (телепортироваться с 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

Python, 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 в разрешенных шагах спасения.

  • Set list [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

Python 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.