¡Salva al piloto!

12

Pregunta

Somos capturados por el ejército robot en su estación espacial. Nuestro piloto de nave espacial está en la prisión, que está en el nivel 1. Solo hay una forma de escapar y está rescatando a tu piloto de nave espacial. Significa pasar del nivel N al nivel 1. Sin embargo, debido a que es muy arriesgado, debe llegar a la prisión en la menor cantidad de pasos posible.

Condiciones

  • Hay 4 formas de moverse:

    1. Pasar del nivel N al nivel N - 1 e.g. from 12 to 11
    2. Pasar del nivel N al nivel N + 1 e.g. from 12 to 13
    3. Usa el teletransporte del nivel 2k al nivel k e.g. from 12 to 6
    4. Usa el teletransporte del nivel 3k al nivel k e.g. from 12 to 4
  • Los teletransportes son solo unidireccionales (puede obtener de 12 a 4 pero es imposible llegar de 4 a 12)

  • Cada acción da un paso

Entrada

La entrada debe leerse desde STDIN, o la alternativa más cercana en su lenguaje de programación. La entrada consiste en un número entero ndonde 1 <= n <= 10^8.

Salida

La salida debe ser el número mínimo de pasos necesarios para pasar nal nivel 1.

Ejemplos

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

¡Intente codificar el programa que nos ayudará a salvar a nuestro piloto de nave espacial de la prisión en el menor tiempo posible y regresar a casa!

¡El código más corto ganará!

Thomas Fürst
fuente
77
Es aconsejable (pero no obligatorio) esperar al menos una semana antes de aceptar una respuesta. Incluso si tiene la intención de cambiar la respuesta aceptada si se publica una respuesta más corta en el futuro, otros pueden tener la impresión de que este concurso está "terminado" y se abstendrán de participar.
Dennis
1
Este desafío me recuerda a un juego que solía jugar con mi calculadora: escribía un número que llenaba la pantalla e intentaba dividir entre 2, 3 o 5 tanto como podía, luego sumaba / restaba 1 y continuaba.
Arcturus

Respuestas:

8

Pyth, 32 bytes

L?<b3tbhSsmm+h%_Wkbdy/+kbd2tS3yQ

Pruébelo en línea: Demostración o conjunto de pruebas

Explicación:

Transformé el problema un poco. Defino 4 nuevas operaciones, que reemplazan las 4 operaciones de la pregunta.

  • level / 2(cuenta como (level % 2) + 1pasos, porque es posible que primero tengas que bajar un nivel para teletransportarte)
  • (level + 1) / 2(cuenta como (level % 2) + 1pasos)
  • level / 3(cuenta como (level % 3) + 1pasos)
  • (level + 1) / 3(cuenta como (-level % 3) + 1pasos)

Por diseño estas operaciones se pueden aplicar a cada número, si el número es 0 mod 2, 1 mod 2, 0 mod 3, 1 mod 3o 2 mod 3.

Puedes pensar fácilmente por qué esto funciona. La idea principal es que hay al menos una solución óptima, que no tiene dos movimientos (hacia abajo) o dos (hacia arriba) en una fila. Prueba: si tiene una solución, que tiene dos de esos movimientos seguidos, puede reemplazarlos y hacer que la solución sea más pequeña o igual en longitud. Por ejemplo, podría reemplazar (subir, subir, teletransportarse de 2k a k) por (teletransportarse de 2k a k, subir) y similares en todos los demás casos.

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

La función yutiliza implícitamente la memorización y, por lo tanto, el tiempo de ejecución no explota.

Jakube
fuente
1
La idea principal es que nunca hay dos (mover hacia abajo) o dos (subir) movimientos en una fila en la solución óptima. - ¿Qué pasa con 29 -> 28 -> 27 -> 9 -> 3 -> 1? Si esa es una solución óptima, ¿cómo decidió que siempre hay una alternativa a la ruta de dos arriba / dos abajo, que no conduce a una región de números más incómoda?
TessellatingHeckler
1
@TessellatingHeckler Quizás debería haber sido más preciso. Hay al menos una solución óptima, que no tiene dos movimientos (hacia abajo) o dos (hacia arriba) en una fila. Por ejemplo, 29 -> 30 -> 10 -> 9 -> 3 -> 1
Jakube
No digo que esté mal, solo que no puedo "pensar fácilmente por qué funciona". Estaba razonando: la ruta más rápida a la habitación 1 está comenzando con una potencia de tres, dividida por tres en cada movimiento. Entonces, la ruta más rápida para los números cerca de una potencia de tres es la resta o suma repetida para llegar a la potencia de tres, luego se divide repetidamente por 3. Si en cambio comienzan moviéndose n / 2, se alejan de la potencia de tres, y por lo tanto más allá de la ruta más rápida posible. No veo cómo encontrarán / siempre / encontrarán otra ruta igualmente corta; parece que ahora están en una región con 'peores' opciones.
TessellatingHeckler
4

Python, 176 bytes

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]

Fuerza bruta hasta el final; Una lista de todos los números 1 to 100,000,000en una computadora de 64 bits es ~ 800Mb de memoria.

El índice de la lista representa los números, los valores representan la distancia desde 1 en los pasos de rescate permitidos.

  • Establecer lista [1] = 0 que significa "accesible en 0 pasos".
  • para cada número de la lista al que se puede acceder en 0 pasos (es decir 1)
    • establecer número + 1, número-1, número * 2, número * 3 accesible en 1 paso
  • para cada número de la lista al que se puede acceder en 1 paso (es decir 0,2,2,3)
    • establecer número + 1, número-1, número * 2, número * 3 accesible en 2 pasos
  • ... etc. hasta que se alcance cada índice de la lista.

El tiempo de ejecución es un poco más de 10 minutos. *Ejem*.

Comentarios de código

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.

Otro

  • Si lo ejecuta en PythonWin, puede acceder a la lista L en el intérprete después.
  • Cada habitación tiene un camino hacia el capitán en 30 movimientos o menos.
  • Solo una habitación está a 30 mudas de distancia, la habitación 72,559,411, y hay 244 habitaciones a 29 mudanzas de distancia.
  • Puede tener características de tiempo de ejecución terribles para el caso máximo, pero uno de los comentarios de la pregunta es " @Geobits todos los programas que deberían encontrar formas más cortas para 20000 casos de prueba en 5 minutos " y prueba 1-20,001 en <6 segundos.
TessellatingHeckler
fuente
2

Python 2 ... 1050

pobremente escrito, sin golf, no óptimo.

Lee el nivel de inicio en stdin, imprime el número mínimo de pasos en 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())
dieter
fuente
2

Código de máquina x86 de 32 bits, 59 bytes

En hexadecimal:

31c9487435405031d231dbb103f7f14a7c070f94c301d008d353e8e3ffffff5b00c3585331d2b102f7f152e8d2ffffff5a00d05a38d076019240c3

El lenguaje de máquina per se no tiene concepto de entrada estándar. Como el desafío es puramente computacional, opté por escribir una función tomando el parámetro de entrada EAXy devolviendo el resultado AL.

@Jakube explica muy bien la matemática detrás del código: la búsqueda se lleva a cabo solo entre los caminos que tienen telepuertos intercalados con no más de un movimiento de un nivel. El rendimiento es de aproximadamente 12000 casos de prueba por segundo en el extremo inferior del rango de entrada y 50 casos por segundo en el extremo superior. El consumo de memoria es de 12 bytes de espacio de pila por nivel de recursión.

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           
meden
fuente