Salve o piloto!

12

Questão

Somos capturados pelo exército de robôs em sua estação espacial. Nosso piloto de nave espacial está na prisão, no nível 1. Há apenas uma maneira de escapar e resgatar seu piloto de nave espacial. Isso significa passar do nível N para o nível 1. No entanto, como é muito arriscado, você precisa chegar à prisão no menor número de etapas possível.

Condições

  • Existem 4 maneiras de mover:

    1. Mover do nível N para o nível N - 1 e.g. from 12 to 11
    2. Mover do nível N para o nível N + 1 e.g. from 12 to 13
    3. Use o teleporte do nível 2k para o nível k e.g. from 12 to 6
    4. Use o teleporte do nível 3k para o nível k e.g. from 12 to 4
  • O teleporte é apenas de mão única (você pode ir de 12 a 4, mas é impossível ir de 4 a 12)

  • Toda ação dá um passo

Entrada

A entrada deve ser lida em STDIN, ou a alternativa mais próxima em sua linguagem de programação. A entrada consiste em um número inteiro nonde 1 <= n <= 10^8.

Resultado

A saída deve ser o número mínimo de etapas necessárias para ir npara o nível 1.

Exemplos

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

Tente codificar um programa que nos ajudará a salvar o piloto da nave espacial da prisão no menor tempo possível e voltar para casa!

O código mais curto vencerá!

Thomas Fürst
fonte
7
É aconselhável (mas não obrigatório) esperar pelo menos uma semana antes de aceitar uma resposta. Mesmo que você pretenda alterar a resposta aceita se uma mais curta for postada no futuro, outras pessoas podem ter a impressão de que o concurso está "acabado" e não participar.
Dennis
1
Esse desafio me lembra um jogo que eu costumava jogar com minha calculadora: digitava um número que preenche a tela e tentava dividir por 2, 3 ou 5 o máximo possível, adicionando / subtraindo 1 e continuando.
quer

Respostas:

8

Pitão, 32 bytes

L?<b3tbhSsmm+h%_Wkbdy/+kbd2tS3yQ

Experimente on-line: Demonstration or Test Suite

Explicação:

Transformei o problema um pouco. Eu defino 4 novas operações, que substituem as 4 operações da questão.

  • level / 2(conta como (level % 2) + 1etapas, porque você pode precisar descer um nível primeiro para se teletransportar)
  • (level + 1) / 2(conta como (level % 2) + 1etapas)
  • level / 3(conta como (level % 3) + 1etapas)
  • (level + 1) / 3(conta como (-level % 3) + 1etapas)

Pelo projeto estas operações podem ser aplicados a cada número, se o número for 0 mod 2, 1 mod 2, 0 mod 3, 1 mod 3ou 2 mod 3.

Você pode pensar facilmente sobre por que isso funciona. A idéia principal é que exista pelo menos uma solução ideal, que não tenha dois movimentos (descer) ou dois (subir) movimentos consecutivos. Prova: se você tiver uma solução com dois movimentos seguidos, poderá substituí-los e tornar a solução menor ou igual em comprimento. Por exemplo, você pode substituir (subir, subir, teleportar de 2k para k) por (teleportar de 2k para k, subir) e similar em todos os outros 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

A função yusa memorização implicitamente e, portanto, o tempo de execução não explode.

Jakube
fonte
1
A idéia principal é que nunca existam dois movimentos (descer) ou dois (subir) movimentos seguidos na solução ideal. - e quanto a 29 -> 28 -> 27 -> 9 -> 3 -> 1? Se essa é uma solução ideal, como você decidiu que sempre existe uma alternativa para o caminho de dois para cima / dois para baixo, que não leva a uma região de números mais complicada?
TessellatingHeckler
1
@TessellatingHeckler Talvez eu devesse ser mais preciso. Há pelo menos uma solução ideal, que não possui dois movimentos (descer) ou dois (mover para cima) seguidos. Exemplo: 29 -> 30 -> 10 -> 9 -> 3 -> 1
Jakube 2/15/15
Não digo que esteja errado, apenas que não consigo "pensar com facilidade por que isso funciona". Eu estava pensando: o caminho mais rápido para a sala 1 está começando com uma potência de três, divida por três a cada movimento. Portanto, a rota mais rápida para números próximos a uma potência de três é subtração ou adição repetida para chegar à potência de três e, em seguida, divida-a repetidamente por 3. Se, em vez disso, começarem movendo o movimento n / 2, eles se afastarão da potência de três, e, portanto, passou a rota mais rápida possível. Não vejo como eles sempre encontrarão outra rota igualmente curta; parece que eles estão em uma região com escolhas 'piores' agora.
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]

Força bruta por todo o caminho; uma lista de todos os números 1 to 100,000,000em um computador de 64 bits é de ~ 800Mb de memória.

O índice da lista representa os números, os valores representam a distância de 1 nas etapas de resgate permitidas.

  • Defina a lista [1] = 0, significando "alcançável em 0 etapas".
  • para cada número da lista acessível em 0 etapas (ou seja 1)
    • definir número + 1, número-1, número * 2, número * 3 alcançável em uma etapa
  • para cada número da lista acessível em 1 etapa (ou seja 0,2,2,3)
    • definir número + 1, número-1, número * 2, número * 3 alcançável em 2 etapas
  • ... etc. até que todos os índices da lista sejam alcançados.

O tempo de execução é um pouco superior a 10 minutos. * ahem *.

Comentários 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.

De outros

  • Se você executá-lo no PythonWin, poderá acessar a lista L no intérprete posteriormente.
  • Cada quarto tem um caminho para o capitão em 30 movimentos ou menos.
  • Apenas um quarto fica a 30 movimentos - o quarto 72.559.411 - e existem 244 quartos, a 29 movimentos.
  • Pode ter características de tempo de execução terríveis para o caso máximo, mas uma das perguntas é " @Geobits todos os programas que devem encontrar maneiras mais curtas para 20000 casos de teste em 5 minutos " e testa 1-20.001 em <6 segundos.
TessellatingHeckler
fonte
2

Python 2 ... 1050

mal escrito, não-destruído, não ótimo.

Lê o nível inicial no stdin, imprime o número mínimo de etapas no 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
fonte
2

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

Em hexadecimal:

31c9487435405031d231dbb103f7f14a7c070f94c301d008d353e8e3ffffff5b00c3585331d2b102f7f152e8d2ffffff5a00d05a38d076019240c3

A linguagem da máquina em si não tem conceito de entrada padrão. Como o desafio é puramente computacional, optei por escrever uma função utilizando o parâmetro de entrada EAXe retornando o resultado AL.

A matemática por trás do código é bem explicada por @Jakube: a pesquisa é conduzida apenas entre os caminhos com teleporte intercalado com não mais que um movimento de nível único. O desempenho é de cerca de 12.000 casos de teste por segundo na extremidade inferior do intervalo de entrada e 50 casos por segundo na extremidade superior. O consumo de memória é de 12 bytes de espaço na pilha por nível de recursão.

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
fonte