1D Hopping Array Maze


17

Inspiré par We do tower hopping et lié au 2D Maze Minus 1D

introduction

Votre tâche consiste à trouver le chemin le plus court pour sortir d'un labyrinthe de tableaux en suivant les règles spécifiées.

Défi

Un tableau 1D a avec n éléments peut être considéré comme un labyrinthe composé de n points, où le point d'index k est connecté aux points avec k + a [ k ] et k - a [ k ] d'une manière unidirectionnelle. En d'autres termes, vous pouvez sauter en avant ou en arrière exactement à [ k ] pas du point d'index k . Les points avec un index en dehors des limites du tableau sont considérés en dehors du labyrinthe.

Pour illustrer cela, considérez le tableau suivant,

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

Si nous sommes au 5ème élément en ce moment, puisque l'élément est 4, nous pouvons sauter 4 pas en avant vers le 9ème élément, ou 4 pas en arrière vers le 1er élément. Si nous faisons ce dernier, nous nous retrouvons avec l'élément 0, ce qui indique qu'aucun autre mouvement n'est possible. Si nous faisons le premier, puisque le 9ème élément est 2, nous pouvons choisir de sauter au 11ème élément, qui est à nouveau un 2, puis nous pouvons à nouveau sauter au "13ème élément", qui est hors des limites du tableau et considéré comme une sortie vers le labyrinthe.

Donc, si nous partons de l'élément du milieu, une façon de sortir du labyrinthe est de sauter 1 pas en arrière, 4 pas en avant, 2 pas en avant et encore 2 pas en avant, qui peut être exprimé comme le tableau [-1,4,2,2]. Sinon , vous pouvez l' exprimer avec le tableau [4,8,10,12]qui enregistre l'indice de base zéro de tous les points intermédiaires et finaux (1 à base d'indice est aussi très bien), ou tout simplement les signes, [-1,1,1,1].

Échapper au labyrinthe de l'extrémité à faible indice est également OK.

Utiliser la première notation et partir du même élément, [1,1,1,2,2]est également une solution mais elle n'est pas optimale car il y a 5 étapes au lieu de 4.

La tâche consiste à trouver le chemin le plus court pour sortir du labyrinthe de tableaux et sortir le chemin. S'il existe plusieurs chemins optimaux, vous pouvez en générer un ou tous. S'il n'y a pas de solution, vous devez sortir une valeur de falsification choisie par vous qui est discernable à partir d'un chemin valide (la production d'aucune sortie du tout est également OK).

Par souci de simplicité, le nombre d'éléments dans le tableau est toujours un nombre impair et nous partons toujours de l'élément du milieu.

Cas de test

Les cas de test illustrent différentes formes de sortie, mais vous n'êtes pas limité à celles-ci.

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

Spécifications

  • Vous pouvez écrire une fonction ou un programme complet.

  • Le tableau contient uniquement des entiers non négatifs.

  • Vous pouvez prendre des entrées et des sorties via n'importe quel formulaire standard , mais veuillez spécifier dans votre réponse le formulaire que vous utilisez.

  • C'est le , le plus petit nombre d'octets gagne.

  • Comme d'habitude, les failles par défaut s'appliquent ici.


Est-il correct de sortir un tableau imbriqué même si la réponse est unique? (par exemple pour [0,8,5,9,4,1,1,1,2,1,2], sortie [[-1,4,2,2]])
Bubbler

@Bubbler Oui, vous pouvez sortir un tableau imbriqué.
Weijun Zhou

Est-il correct de retourner le chemin d'échappement dans l'ordre inverse. Alors [1,1,1,-1]au lieu de [-1,1,1,1]?
Ton Hospel

@TonHospel Oui, dites-le simplement dans votre réponse.
Weijun Zhou

le cas de test 2 semble incorrect, pourriez-vous l'expliquer?
edc65

Réponses:


3

JavaScript (ES6), 117 octets

Renvoie un tableau de points intermédiaires et finaux indexés 0, ou un tableau vide s'il n'existe aucune solution.

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

Essayez-le en ligne!

Commenté

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

Husk , 22 octets

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

Renvoie une liste de signes ou une liste vide si aucune solution n'existe. Essayez-le en ligne!

Explication

Il s'agit d'une solution de force brute qui vérifie les listes -1,0,1de plus en plus longues et renvoie la première qui entraîne un saut hors du tableau. Puisqu'il est de longueur minimale, il ne contiendra pas de 0.

ḟȯ¬€ŀ¹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 179 octets

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]

Essayez-le en ligne!

Éditer:

  • Sauvegardé 9 octets par all(..)and s => all(..)*s, if u not in x => if{u}-x
    le premier exploite boolean * list == int * list, le second utilise la différence de set (le set vide est également faux).

Format de sortie: tableau imbriqué de toutes les réponses optimales, donné sous forme d'indices de base zéro de points intermédiaires et finaux.

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

L'algorithme est un simple BFS. senregistre tous les ichemins de longueur possibles suri itération, à l'exclusion des indices déjà visités. Notez que la notation en étoile étendue est (ab) utilisée car l'accès répété au tableau est coûteux. J'ai trouvé qu'une telle notation peut également réduire certains espaces blancs si elle est utilisée correctement.

J'ai également fait une version récursive (mais plus longue) de la solution ci-dessus. Les deux s andet or ssont nécessaires, sinon cela ne fonctionne pas.

Python 3 , 210 octets

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

Essayez-le en ligne!


2

Haskell , 207 202 octets

5 octets économisés grâce à 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)]]

Essayez-le en ligne!

Il s'agit d'une fonction qui prend une liste de Intcomme paramètre et renvoie une liste de chemins où chaque chemin est une liste de sauts relatifs effectués pour sortir du tableau.

La version non golfée:

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 octets

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

Essayez-le en ligne!

Initialement essayé une recherche de retour en arrière récursif, car utiliser mainpour la récursivité est toujours amusant. En fin de compte, une simple recherche en largeur non récursive a pu être réduite, ce qui est le cas de cette version. Ce programme prend le tableau d'entrée comme arguments de ligne de commande, sans accolades, par exemple 0 8 5 9 4 1 1 1 2 1 2pour le premier exemple fourni. Le programme affiche sur stdout une liste d' indexées sur 1 , les indices de tableau délimité par des virgules dans l' ordre inverse, en commençant par le dernier, en dehors des limites du terrain / « échappé » index et le dos de travail à travers les indices intermédiaires atteints (il ne reproduit pas le centre, indice de départ). Le programme ne produit pas d'accolades autour du tableau et laisse une virgule de fin car séparéprintfles instructions prennent beaucoup de caractères. La sortie correspondant au premier exemple de test ci-dessus est 13,11,9,5,par exemple.

S'il n'y a pas de chemin d'échappement du labyrinthe de tableaux, le programme ne produit rien.

Dégolfé et expliqué qu'il est ci-dessous (fortement dégouliné avec quelques changements de lisibilité):

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

Comme d'habitude pour le code C golfé, la sortie de la compilation comprendra bien sûr un mur convivial d'avertissements et de notes.



1

Perl 5 , -a: 73 octets

(comptage à l'ancienne: 75 octets, +1pour aet +1pour remplacer -//par -/$/et utiliser $`pour $')

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

Donnez le tableau d'entrée en une seule ligne sur STDIN, par exemple 0 8 5 9 4 1 1 1 2 1 2

imprime les positions visitées dans l'ordre inverse, y compris le point de départ, puis se bloque

N'imprime rien s'il n'y a pas de solution

Essayez-le en ligne!


1

Rubis , 102 octets

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

Essayez-le en ligne!

Prend le labyrinthe d'entrée sous forme de tableau, les sorties en imprimant le chemin d'échappement à l'envers, de la sortie au point de départ (inclus). N'imprime rien s'il n'y a pas d'échappatoire.

Cette approche abuse de la méthode de la carte pour itérer à travers un tableau temporaire stockant l'historique des chemins, qui est constamment étendu à la volée chaque fois qu'il y a une autre étape possible à prendre.

En principe, je pourrais enregistrer un autre octet en utilisant à la return xplace de break p x, mais cela signifierait affirmer que ma valeur de falsification est égale à tous les déchets monstrueux stockés dans b. Ce serait probablement trop, même compte tenu de la flexibilité permise de la sortie ...

Procédure pas à pas

->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
  }  
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.