Salva il pilota!


12

Domanda

Siamo catturati dall'esercito di robot nella loro stazione spaziale. Il nostro pilota di navi spaziali è nella prigione che si trova al livello 1. C'è solo un modo per fuggire e sta salvando il tuo pilota di navi spaziali. Significa passare dal livello N al livello 1. Tuttavia, poiché è molto rischioso, devi raggiungere la prigione nel minor numero di passaggi possibile.

condizioni

  • Ci sono 4 modi per spostarsi:

    1. Passa dal livello N al livello N - 1 e.g. from 12 to 11
    2. Passa dal livello N al livello N + 1 e.g. from 12 to 13
    3. Usa il teletrasporto dal livello 2k al livello k e.g. from 12 to 6
    4. Usa il teletrasporto dal livello 3k al livello k e.g. from 12 to 4
  • I teletrasporti sono solo a senso unico (puoi ottenere dalle 12 alle 4 ma è impossibile ottenere dalle 4 alle 12)

  • Ogni azione fa un passo

Ingresso

L'input deve essere letto da STDIN o l'alternativa più vicina al linguaggio di programmazione. L'input è costituito da un numero intero in ncui 1 <= n <= 10^8.

Produzione

L'output dovrebbe essere il numero minimo di passaggi necessari per passare dal nlivello 1.

Esempi

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

Prova a programmare il codice che ci aiuterà a salvare il nostro pilota di navi spaziali dalla prigione nel più breve tempo possibile e tornare a casa!

Vincerà il codice più corto!


7
È consigliabile (ma non obbligatorio) attendere almeno una settimana prima di accettare una risposta. Anche se hai intenzione di cambiare la risposta accettata se una più breve verrà pubblicata in futuro, altri potrebbero avere l'impressione che questo concorso sia "finito" e astenersi dal partecipare.
Dennis,

1
Questa sfida mi ricorda un gioco che usavo con la mia calcolatrice: avrei digitato un numero che riempie lo schermo e proverei a dividere il più possibile per 2, 3 o 5, quindi aggiungendo / sottraendo 1 e continuando.
Arcturus,

Risposte:


8

Pyth, 32 byte

L?<b3tbhSsmm+h%_Wkbdy/+kbd2tS3yQ

Provalo online: dimostrazione o Test Suite

Spiegazione:

Ho trasformato un po 'il problema. Definisco 4 nuove operazioni, che sostituiscono le 4 operazioni della domanda.

  • level / 2(conta come (level % 2) + 1passaggi, perché potrebbe essere necessario prima spostarsi di un livello in basso per teletrasportarsi)
  • (level + 1) / 2(conta come (level % 2) + 1passaggi)
  • level / 3(conta come (level % 3) + 1passaggi)
  • (level + 1) / 3(conta come (-level % 3) + 1passaggi)

Di progettazione queste operazioni possono essere applicate a ciascun numero, se il numero è 0 mod 2, 1 mod 2, 0 mod 3, 1 mod 3o 2 mod 3.

Puoi facilmente pensare, perché funziona. L'idea principale è che esiste almeno una soluzione ottimale, che non ha due movimenti (sposta in basso) o due (sposta in alto) di seguito. Prova: se hai una soluzione, che ha due di questi movimenti di seguito, puoi sostituirli e rendere la soluzione più piccola o uguale in lunghezza. Ad esempio, è possibile sostituire (spostarsi verso l'alto, spostarsi verso l'alto, teletrasportarsi da 2k a k) con (teletrasportarsi da 2k a k, spostarsi verso l'alto) e simili in tutti gli altri casi.

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 funzione yutilizza implicitamente la memoizzazione e pertanto il runtime non esplode.


1
L'idea principale è che non ci sono mai due (sposta in basso) o due (sposta in alto) di fila nella soluzione ottimale. - che dire di 29 -> 28 -> 27 -> 9 -> 3 -> 1? Se questa è una soluzione ottimale, come hai deciso che esiste sempre un'alternativa al percorso due-su / due-giù, che non porta a una regione di numeri più scomoda?
Tessellating Heckler,

1
@TessellatingHeckler Forse avrei dovuto essere più preciso. Esiste almeno una soluzione ottimale, che non ha due (sposta giù) o due (sposta su) movimenti di fila. Ad esempio 29 -> 30 -> 10 -> 9 -> 3 -> 1
Jakube

Non dico che è sbagliato, solo che non riesco a "facilmente pensare al perché funzioni". Stavo ragionando: il percorso più veloce per la stanza 1 inizia con una potenza di tre, dividere per tre ogni mossa. Quindi il percorso più veloce per i numeri vicino a una potenza di tre è la sottrazione ripetuta o l'addizione per arrivare alla potenza di tre, quindi si dividono ripetutamente per 3. Se invece iniziano muovendosi muovendo n / 2, si allontanano ulteriormente dalla potenza di tre, e quindi oltre il percorso più veloce possibile. Non vedo come troveranno / sempre / troveranno un altro percorso altrettanto breve; sembra che ora si trovino in una regione con scelte "peggiori".
TessellatingHeckler,

4

Python, 176 byte

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]

Forza bruta fino in fondo; un elenco di tutti i numeri 1 to 100,000,000su un computer a 64 bit è di ~ 800 Mb di memoria.

L'indice dell'elenco rappresenta i numeri, i valori rappresentano la distanza da 1 nelle fasi di salvataggio consentite.

  • Imposta elenco [1] = 0 che significa "raggiungibile in 0 passaggi".
  • per ogni numero nell'elenco che è raggiungibile in 0 passaggi (es. 1)
    • imposta il numero + 1, il numero 1, il numero * 2, il numero * 3 raggiungibile in 1 passaggio
  • per ogni numero nell'elenco che è raggiungibile in 1 passaggio (es. 0,2,2,3)
    • imposta il numero + 1, il numero 1, il numero * 2, il numero * 3 raggiungibile in 2 passaggi
  • ... ecc. fino al raggiungimento di ogni indice di elenco.

Il tempo di esecuzione è di poco più di 10 minuti. * Ahem *.

Commenti sul codice

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.

Altro

  • Se lo esegui in PythonWin, puoi successivamente accedere all'elenco L nell'interprete.
  • Ogni stanza ha un percorso per il capitano in 30 mosse o meno.
  • Solo una stanza dista 30 mosse - stanza 72.559.411 - e ci sono 244 stanze distanti 29 mosse.
  • Può avere caratteristiche di runtime terribili per il caso massimo, ma uno dei commenti alla domanda è " @Geobits tutti i programmi che dovrebbero trovare i modi più brevi per 20000 casi di test in 5 minuti " e test 1-20.001 in <6 secondi.

2

Python 2 ... 1050

scritto male, non golfato, non ottimale.

Legge il livello iniziale su stdin, stampa il numero minimo di passaggi su 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

Codice macchina x86 a 32 bit, 59 byte

In esadecimale:

31c9487435405031d231dbb103f7f14a7c070f94c301d008d353e8e3ffffff5b00c3585331d2b102f7f152e8d2ffffff5a00d05a38d076019240c3

Il linguaggio macchina di per sé non ha alcun concetto di input standard. Poiché la sfida è pura computazionale, ho optato per scrivere una funzione che accetta parametri di input EAXe restituisce risultati AL.

La matematica dietro il codice è ben spiegata da @Jakube: la ricerca viene condotta solo tra i percorsi con teletrasporto intervallati da non più di una mossa di un livello. Le prestazioni sono di circa 12000 casi di test al secondo nella parte inferiore dell'intervallo di input e 50 casi al secondo nella parte superiore. Il consumo di memoria è di 12 byte di spazio stack per livello di ricorsione.

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           
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.