Laberinto de saltos 1D

17

Inspirado en Realizamos saltos de torre y relacionados con 2D Maze Minus 1D

Introducción

Su tarea es encontrar la ruta más corta para salir de un laberinto de matrices siguiendo las reglas especificadas.

Desafío

Una matriz 1D a con n elementos puede considerarse como un laberinto compuesto por n puntos, donde el punto con índice k está conectado a los puntos con k + a [ k ] y k - a [ k ] de una sola manera. En otras palabras, puede saltar hacia adelante o hacia atrás exactamente a [ k ] pasos desde el punto con el índice k . Los puntos con un índice fuera de los límites de la matriz se consideran fuera del laberinto.

Para ilustrar esto, considere la siguiente matriz,

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

Si estamos en el quinto elemento en este momento, dado que el elemento es 4, podemos saltar 4 pasos hacia adelante hasta el noveno elemento, o 4 pasos hacia atrás hasta el primer elemento. Si hacemos lo último, terminamos con el elemento 0, que indica que no son posibles más movimientos. Si hacemos lo primero, dado que el noveno elemento es 2, podemos elegir saltar al elemento 11, que nuevamente es un 2, y luego podemos saltar nuevamente al "elemento 13", que está fuera de los límites del matriz y consideró una salida al laberinto.

Entonces, si comenzamos desde el elemento en el medio, una forma de salir del laberinto es saltar 1 paso hacia atrás, 4 pasos hacia adelante, 2 pasos hacia adelante y nuevamente 2 pasos hacia adelante, que se pueden expresar como la matriz [-1,4,2,2]. Alternativamente se puede expresar con la matriz [4,8,10,12]que registra el índice basado en cero de todos los puntos intermedios y finales (1 basado en índices es también fino), o sólo los signos, [-1,1,1,1].

Escapar del laberinto desde el extremo de bajo índice también está bien.

Usar la primera notación y comenzar desde el mismo elemento [1,1,1,2,2]también es una solución, pero no es óptima ya que hay 5 pasos en lugar de 4.

La tarea es encontrar la ruta más corta para salir del laberinto de la matriz y generar la ruta. Si hay más de una ruta óptima, puede generar una o todas ellas. Si no hay una solución, debe generar un valor falso elegido por usted que sea discernible a partir de una ruta válida (no generar ningún resultado también está bien).

Para simplificar, el número de elementos en la matriz siempre es un número impar y siempre comenzamos desde el elemento en el medio.

Casos de prueba

Los casos de prueba ilustran varias formas de salida, pero no está limitado a estas.

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]
[]

Especificaciones

  • Puede escribir una función o un programa completo.

  • La matriz solo contiene enteros no negativos.

  • Puede tomar entrada y salida a través de cualquier formulario estándar , pero especifique en su respuesta qué formulario está utilizando.

  • Este es el , gana el menor número de bytes.

  • Como de costumbre, las lagunas predeterminadas se aplican aquí.

Weijun Zhou
fuente
¿Está bien generar una matriz anidada incluso si la respuesta es única? (por ejemplo [0,8,5,9,4,1,1,1,2,1,2], para salida [[-1,4,2,2]])
Bubbler
@Bubbler Sí, puede generar una matriz anidada.
Weijun Zhou
¿Está bien devolver la ruta de escape en orden inverso? Entonces, en [1,1,1,-1]lugar de [-1,1,1,1]?
Ton Hospel
@TonHospel Sí, solo dilo en tu respuesta.
Weijun Zhou
el caso de prueba 2 parece incorrecto, ¿podría explicarlo?
edc65

Respuestas:

3

JavaScript (ES6), 117 bytes

Devuelve una matriz de puntos intermedios y finales indexados en 0, o una matriz vacía si no existe una solución.

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

Pruébalo en línea!

Comentado

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
Arnauld
fuente
3

Casco , 22 bytes

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

Devuelve una lista de signos o una lista vacía si no existe una solución. Pruébalo en línea!

Explicación

Esta es una solución de fuerza bruta que comprueba las listas -1,0,1de mayor longitud y devuelve la primera que resulta en un salto fuera de la matriz. Como tiene una longitud mínima, no contendrá 0s.

ḟȯ¬€ŀ¹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.
Zgarb
fuente
2

Python 3 , 195 188 179 bytes

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]

Pruébalo en línea!

Editar:

  • Guardado 9 bytes por all(..)and s => all(..)*s, if u not in x => if{u}-x
    El primero explota boolean * list == int * list, el último usa la diferencia de conjunto (el conjunto vacío también es falso).

Formato de salida: conjunto anidado de todas las respuestas óptimas, dado como índices basados ​​en cero de puntos intermedios y finales.

Por ejemplo f([0,8,5,9,4,1,1,1,2,1,2]) == [[4, 8, 10, 12]].

El algoritmo es simple BFS. sregistra todas las irutas de longitud posibles en ila iteración, excluyendo los índices ya visitados. Tenga en cuenta que la notación en estrella extendida se usa (ab) porque el acceso repetido a la matriz es costoso. Descubrí que dicha notación también puede reducir algunos espacios en blanco si se usa correctamente.

También hice una versión recursiva (pero más larga) de la solución anterior. Ambos s andy or sson necesarios, de lo contrario no funciona.

Python 3 , 210 bytes

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

Pruébalo en línea!

Bubbler
fuente
2

Haskell , 207 202 bytes

5 bytes guardados gracias 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)]]

Pruébalo en línea!

Esta es una función que toma una lista Intcomo parámetro y devuelve una lista de rutas donde cada ruta es una lista de saltos relativos tomados para salir de la matriz.

La versión sin golf:

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
Cristian Lupascu
fuente
2

C (gcc) , 269 bytes

#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}}}}

Pruébalo en línea!

Inicialmente probé una búsqueda recursiva de retroceso, porque usar mainpara la recursividad siempre es divertido. Al final, sin embargo, se pudo hacer una búsqueda directa no recursiva de amplitud, que es lo que es esta versión. Este programa toma la matriz de entrada como argumentos de línea de comandos, sin llaves, por ejemplo, 0 8 5 9 4 1 1 1 2 1 2para el primer ejemplo proporcionado. El programa genera en stdout una lista de índices de matriz delimitados por comas indexados en 1 en orden inverso, comenzando desde el índice final, fuera de límites / 'escapado' y trabajando nuevamente a través de los índices intermedios alcanzados (no genera el centro, índice inicial). El programa no genera llaves alrededor de la matriz y deja una coma final porqueprintfLas declaraciones toman muchos caracteres. La salida correspondiente al primer ejemplo de prueba anterior es 13,11,9,5,, por ejemplo.

Si no hay una ruta de escape del laberinto de matrices, el programa no genera nada.

Degolfed y explicó que está debajo (fuertemente desgolfado con algunos cambios para facilitar la lectura):

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;
            }
        }
    }
}

Como es habitual para el código C golfizado, la salida de la compilación incluirá, por supuesto, un muro amistoso de advertencias y notas.

SevenStarConstellation
fuente
253 bytes
ceilingcat
1

Perl 5 , -a: 73 bytes

(conteo de estilo antiguo: 75 bytes, +1para ay +1para la sustitución de -//por -/$/y utilizar $`para $')

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

Dé la matriz de entrada como una línea en STDIN, por ejemplo 0 8 5 9 4 1 1 1 2 1 2

imprime las posiciones visitadas en orden inverso, incluido el punto de partida, luego se bloquea

No imprime nada si no hay solución

Pruébalo en línea!

Ton Hospel
fuente
1

Ruby , 102 bytes

->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)}}

Pruébalo en línea!

Toma el laberinto de entrada como una matriz, e imprime la ruta de escape en reversa, desde la salida hasta el punto de partida (inclusive). No imprime nada si no hay escapatoria.

Este enfoque hace un mal uso del método de mapa para iterar a través de una matriz temporal que almacena el historial de rutas, que se extiende constantemente sobre la marcha cada vez que hay otro posible paso a seguir.

En principio, podría guardar otro byte usando en return xlugar de break p x, pero eso significaría afirmar que mi valor falso es igual a toda la basura monstruosa almacenada b. Probablemente, esto sería demasiado, incluso teniendo en cuenta la flexibilidad permitida de la salida ...

Tutorial

->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
  }  
}
Kirill L.
fuente