Labirinto 1D Hopping Array


17

Ispirato da We hopping tower e relativo a 2D Maze Minus 1D

introduzione

Il tuo compito è trovare il percorso più breve per uscire da un labirinto di array seguendo le regole specificate.

Sfida

Un array 1D a con n elementi può essere considerato come un labirinto composto da n punti, in cui il punto con indice k è collegato ai punti con k + a [ k ] e k - a [ k ] in un modo unidirezionale. In altre parole, puoi saltare avanti o indietro esattamente a [ k ] passi dal punto con indice k . I punti con un indice al di fuori dei limiti dell'array sono considerati al di fuori del labirinto.

Per illustrare questo, considera il seguente array,

[0,8,5,9,4,1,1,1,2,1,2]

Se siamo al 5 ° elemento in questo momento, poiché l'elemento è 4, possiamo saltare 4 passi in avanti al 9 ° elemento o 4 passi indietro al 1 ° elemento. Se facciamo quest'ultima, finiamo con l'elemento 0, che indica che non sono possibili ulteriori mosse. Se facciamo il primo, dato che il 9 ° elemento è 2, possiamo scegliere di saltare all'11 ° elemento, che è di nuovo un 2, e quindi possiamo saltare di nuovo al "13 ° elemento", che è fuori dai limiti del matrice e considerata un'uscita nel labirinto.

Quindi, se partiamo dall'elemento nel mezzo, un modo per uscire dal labirinto è saltare 1 passo indietro, 4 passi avanti, 2 passi avanti e ancora 2 passi avanti, che possono essere espressi come array [-1,4,2,2]. In alternativa si può esprimere con la matrice [4,8,10,12]che registra l'indice a base zero di tutti i punti intermedi e finali (1-based indice è anche bene), o solo i segni, [-1,1,1,1].

Anche scappare dal labirinto dall'estremità a basso indice è OK.

Usare la prima notazione e partire dallo stesso elemento, [1,1,1,2,2]è anche una soluzione ma non è ottimale poiché ci sono 5 passaggi anziché 4.

Il compito è scoprire il percorso più breve per uscire dal labirinto dell'array e produrre il percorso. Se esistono più percorsi ottimali, è possibile emetterne uno o tutti. Se non ci sono soluzioni, dovresti emettere un valore falso scelto da te che è distinguibile da un percorso valido (anche la produzione di nessun output è OK).

Per semplicità, il numero di elementi nell'array è sempre un numero dispari e partiamo sempre dall'elemento nel mezzo.

Casi test

I casi di test illustrano varie forme di output, ma non si è limitati a questi.

Input
Output

[0,8,5,9,4,1,1,1,2,1,2]
[-1,4,2,2]

[2,3,7,1,2,0,2,8,9]
[2,9] (or [2,-5] or [[2,9],[2,-5]])

[0,1,2,2,3,4,4,4,3,2,2,3,0]
[1,-1,1,1]

[0,1,2,2,4,4,6,6,6,6,6,4,2,1,2,2,0]
[]

Specifiche

  • È possibile scrivere una funzione o un programma completo.

  • L'array contiene solo numeri interi non negativi.

  • Puoi prendere input e output attraverso qualsiasi modulo standard , ma specifica nella tua risposta quale modulo stai utilizzando.

  • Questo è , vince il numero più basso di byte.

  • Come al solito, qui si applicano scappatoie predefinite .


Va bene produrre un array nidificato anche se la risposta è unica? (es. per [0,8,5,9,4,1,1,1,2,1,2], in uscita [[-1,4,2,2]])
Bubbler

@Bubbler Sì, è possibile generare array nidificati.
Weijun Zhou,

È corretto restituire la via di fuga in ordine inverso. Quindi [1,1,1,-1]invece di [-1,1,1,1]?
Ton Hospel,

@TonHospel Sì, basta dirlo nella tua risposta.
Weijun Zhou,

il test case 2 sembra errato, potresti spiegarlo?
edc65,

Risposte:


3

JavaScript (ES6), 117 byte

Restituisce una matrice di punti intermedi e finali indicizzati 0 o una matrice vuota se non esiste alcuna soluzione.

a=>(g=(x,p,d=a[x])=>1/d?[d,-d].map(d=>p.includes(X=x+d)||g(X,[...p,X])):o=o==''|o[p.length]?p:o)(a.length>>1,o=[])&&o

Provalo online!

Commentate

a =>                              // given the maze a[]
  (g = (                          // g = recursive function taking:
    x,                            //   x = current position
    p,                            //   p[] = list of visited cells
    d = a[x]                      //   d = value of current cell
  ) =>                            //
    1 / d ?                       // if d is defined:
      [d, -d].map(d =>            //   for d and -d:
        p.includes(X = x + d) ||  //     if the cell at X = x + d was not yet visited,
        g(X, [...p, X])           //     do a recursive call to g() at this position
      )                           //   end of map()
    :                             // else:
      o =                         //   update o:
        o == '' |                 //     if o was empty
        o[p.length] ?             //     or p is shorter than o:
          p                       //       set o to p
        :                         //     else:
          o                       //       let o unchanged
  )(a.length >> 1, o = [])        // initial call to g(), starting in the middle
  && o                            // return o

3

Buccia , 22 byte

ḟȯ¬€ŀ¹FS+o*!¹⌈½L¹ṁπṡ1ŀ

Restituisce un elenco di segni o un elenco vuoto se non esiste una soluzione. Provalo online!

Spiegazione

Questa è una soluzione a forza bruta che controlla gli elenchi -1,0,1di lunghezza crescente e restituisce il primo che provoca un salto fuori dall'array. Poiché ha una lunghezza minima, non conterrà 0 secondi.

ḟȯ¬€ŀ¹FS+o*!¹⌈½L¹ṁπṡ1ŀ  Implicit input, say A = [0,1,1]
                     ŀ  Indices of A: [1,2,3]
                 ṁ      Map over them and concatenate:
                  π      Cartesian power
                   ṡ1    of the symmetric range [-1,0,1].
                        Result is B = [[-1],[0],[1],[-1,-1],...,[1,1,1]]
ḟ                       Find the first element of B that satisfies this:
                         Argument is a list, say C = [1,-1].
      F                  Reduce C from the left
             ⌈½L¹        using ceil(length(A)/2) as the initial value
       S+o*!¹            with this function:
                          Arguments are an index of A, say I = 2, and a sign, say S = 1.
           !¹             The element of A at I: 1
         o*               Multiply by S: 1
       S+                 Add to I: 2
                         At the end of the reduction, we have a number I, here 2.
   €ŀ¹                   Is it an element of the indices of A: Yes.
 ȯ¬                      Negate: No.
                        The result is the shortest list C for which I is outside of A.

2

Python 3 , 195 188 179 byte

def f(a):
 v=len(a);x,*s={v//2},[v//2]
 while all(v>b>-1for*c,b in s)*s:s=[x.add(u)or c+[b,u]for*c,b in s for u in[b+a[b],b-a[b]]if{u}-x]
 return[b[1:]for b in s if not-1<b[-1]<v]

Provalo online!

Modificare:

  • Salvato 9 byte da all(..)and s => all(..)*s, if u not in x => if{u}-x
    Il primo sfrutta boolean * list == int * list, il secondo usa la differenza di set (anche il set vuoto è falso).

Formato di output: matrice nidificata di tutte le risposte ottimali, fornite come indici a base zero di punti intermedi e finali.

Ad esempio: f([0,8,5,9,4,1,1,1,2,1,2]) == [[4, 8, 10, 12]].

L'algoritmo è semplice BFS. sregistra tutti i possibili ipercorsi di lunghezza isull'iterazione, esclusi gli indici già visitati. Si noti che la notazione a stella estesa viene (ab) utilizzata perché l'accesso alla matrice ripetuta è costoso. Ho scoperto che una tale notazione può anche ridurre alcuni spazi bianchi se usato correttamente.

Ho anche creato una versione ricorsiva (ma più lunga) con la soluzione di cui sopra. Entrambi s ande or ssono necessari, altrimenti non funziona.

Python 3 , 210 byte

lambda a:[b[1:]for b in g(a,[[len(a)//2]],{len(a)//2})if not-1<b[-1]<len(a)]
g=lambda a,s,x:s and all(-1<b<len(a)for*c,b in s)and g(a,[x.add(u)or c+[b,u]for*c,b in s for u in[b+a[b],b-a[b]]if u not in x],x)or s

Provalo online!


2

Haskell , 207 202 byte

5 byte salvati grazie a BMO .

l=length
x!p|i<-h p,d<-x!!i=[p++[x]|x<-[(-d,i-d),(d,i+d)],x`notElem`p]
x?p|i<-h p=i<0||i>=l x
h=snd.last
x#[]=[]
x#p|l(x%p)<1=x#(p>>=(x!))|1>0=x%p
(%)=filter.(?)
f x=(tail.map fst)<$>x#[[(0,l x`div`2)]]

Provalo online!

Questa è una funzione che accetta un elenco di Intcome parametro e restituisce un elenco di percorsi in cui ogni percorso è un elenco di salti relativi eseguiti per uscire dall'array.

La versione ungolfed:

move :: [Int] -> [(Int, Int)] -> [Path]
move xs path = map(\x->path++[x]) $ filter (\s -> s`notElem`path) $ [(-delta, i-delta), (delta, i+delta)]
  where (_,i) = last path
        delta = xs!!i :: Int

outside :: [Int] -> Path -> Bool
outside xs paths = i < 0 || i >= length xs
  where (_,i) = last paths

shortest' :: [Path] -> [Int] -> [Path]
shortest' paths xs | null paths       = []
                   | not (null ready) = ready
                   | otherwise        = shortest' paths' xs
                   where ready  = filter (outside xs) paths
                         paths' = concatMap (move xs) paths

shortest xs = map tail $ map (map fst) $ shortest' [[(0,length xs`div`2)]] xs

2

C (gcc) , 269 byte

#define A n){for(printf("%d,",n);i^l[i];i=l[i])printf("%d,",x[i]);break;}if(!u[n]){u[n]=x[m]=n;l[m++]=i;
#define M calloc(r,sizeof(s))
*x,*u,*l,s,m=1,i,j,n,w;main(r,v)char**v;{s=r-1;x=M;u=M;l=M;for(*x=1+s/2;i<m;i++){j=x[i];if(w=atoi(v[j])){n=j+w;if(s<A}n=j-w;if(1>A}}}}

Provalo online!

Inizialmente ho provato una ricerca di backtracking ricorsiva, perché l'utilizzo mainper la ricorsione è sempre divertente. Alla fine, anche se una semplice ricerca non ricorsiva in ampiezza è stata in grado di essere ridotta, questo è quello che è questa versione. Questo programma accetta l'array di input come argomenti della riga di comando, senza parentesi graffe, ad esempio 0 8 5 9 4 1 1 1 2 1 2per il primo esempio fornito. Il programma emette su stdout un elenco di indici di array 1 indicizzati , delimitati da virgole in ordine inverso, a partire dall'indice finale, fuori limite / "sfuggito" e passando attraverso gli indici intermedi raggiunti (non genera il centro, indice iniziale). Il programma non genera parentesi graffe attorno all'array e lascia una virgola finale perché separataprintfle dichiarazioni prendono molti personaggi. L'output corrispondente al primo esempio di test sopra è 13,11,9,5,, ad esempio.

Se non esiste una via di fuga dal labirinto dell'array, il programma non emette nulla.

Degolfato e spiegato è di seguito (fortemente degradato con alcune modifiche per la leggibilità):

int *x, *u, *l, s, m = 1, i, j, n, w;                        //Declare all the state we'll need
int main(r, v) char** v;{                            
    s = r - 1;                                               //s is our actual array size, since v[0] is the program name.
    x = calloc(r, sizeof(int));                              //x is an array that will form our BFS queue. Since it is a BFS we've no need to visit any elements more than once (first visit will have been on a shortest route to it), so the amount of space we have here should suffice.
    u = calloc(r, sizeof(int));                              //u is an array that will be used to flag when an array index has been visited; only reason it's int* is for ease of declaration
    l = calloc(r, sizeof(int));                              //l is an array that will be used parallel to x and stores backpointers in the form of indexes into x, which will be used to construct the actual path once it is found.
    x[0] = 1 + (s/2);                                        //Init the first element in the queue to our center index of the array, adding one because of the program name in v/argv.
    for(; i < m; i++) {                                      //m is the number of elements in our BFS queue. It starts at 1 and grows during iteration; if this loop terminates before finding a path there is none.
        j = x[i];                                            //Current index in the array we are examining
        if (w = atoi(v[j])) {                                //Set w to be the actual array value at the current index (and check that it's nonzero since if it isn't we can't get anywhere from here)
            n = j + w;                                       //Try a move in the positive direction
            if (n > s) {                                     //If the move escapes the array
                for(printf("%d,", n); i ^ l[i]; i = l[i]) {  //Print the location escaped to and then loop back through the backpointers to reconstruct the path. The only backpointer that will point to its own queue index is the starting one, so terminate there.
                    printf("%d,", x[i]);                     //Print each intermediate array index
                }
                break;                                       //Then break the outer for loop and exit.
            }
            if(!u[n]) {                                      //If the jump didn't take us out of the array and we haven't visited where it goes to, add it to the queue.
                u[n] = x[m] = n;                             //m is the current tail of the queue, so put this new location there. Since we're 1-indexed and if n was zero we'd have escaped, we know it isn't so can use it to mark this index as visited also.
                l[m++] = i;                                  //Also set the backpointer for this new queue element to point back to the current index, then increment the tail of the queue.
            }
            n = j - w;                                       //Now the backwards move
            if (n < 1) {                                     //Repeat analogous to the forward case.
                for(printf("%d,", n); i ^ l[i]; i = l[i]) {
                    printf("%d,", x[i]);
                }
                break;
            }
            if (!u[n]) {
                u[n] = x[m] = n;
                l[m++] = i;
            }
        }
    }
}

Come al solito per il codice C golfizzato, l'output della compilazione includerà ovviamente un muro amichevole di avvertimenti e note.



1

Perl 5 , -a: 73 byte

(conteggio vecchio stile: 75 byte, +1per ae +1per la sostituzione -//di -/$/e l'utilizzo di $`per $')

#!/usr/bin/perl -a
use 5.10.0;
@;=$#F/2;$v{$^H=$_}//=push@;,map$'+$_*($F[$^H]//1/!say$').$".$',-//,1for@

Fornire l'array di input come una riga su STDIN ad es 0 8 5 9 4 1 1 1 2 1 2

stampa le posizioni visitate in ordine inverso compreso il punto iniziale, quindi si arresta in modo anomalo

Non stampa nulla se non c'è soluzione

Provalo online!


1

Rubino , 102 byte

->a{b=[[a.size>>1]];b.map{|x|(v=a[w=x[0]])&&w>=0?[w-v,w+v].map{|j|x.index(j)?0:b<<[j]+x}:(break p x)}}

Provalo online!

Prende il labirinto di input come un array, emette stampando il percorso di escape al contrario, dall'uscita al punto di partenza (incluso). Non stampa nulla se non c'è via di fuga.

Questo approccio fa un uso improprio del metodo della mappa per iterare attraverso un array temporaneo che memorizza la cronologia dei percorsi, che viene costantemente estesa al volo ogni volta che c'è un altro possibile passo da compiere.

In linea di principio, potrei salvare un altro byte usando return xinvece dibreak p x , ma ciò significherebbe sostenere che il mio valore di falsa è uguale a tutta la mostruosa spazzatura immagazzinata b. Probabilmente, questo sarebbe troppo, anche considerando la flessibilità consentita dell'output ...

Procedura dettagliata

->a{
  b=[[a.size>>1]] #Initialize an array of paths with our starting point index
  b.map{|x|       #Iterate through this array
    (v=a[w=x[0]]) #w is the current point in the path, v is its array value
    &&w>=0        #Ruby's support for negative indexing costs us 6 bytes :(
    ?             #If we are still within the bounds of the maze
      [w-v,w+v].map{|j| #Try moving in both directions
        x.index(j)? #If we have been there before, or stuck on zero
        0         #This is a dead-end, just assign a throwaway value
        :b<<[j]+x #Otherwise push the elongated path on top of our iterator
      } 
    :(break p x)  #Escaped! Exit the loop and report the path
  }  
}
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.