1D-Hopping-Array-Labyrinth


17

Inspiriert von We do Tower Hopping und verwandt mit 2D Maze Minus 1D

Einführung

Ihre Aufgabe ist es, den kürzesten Weg zu finden, um ein Array-Labyrinth nach festgelegten Regeln zu verlassen.

Herausforderung

Ein 1D-Array a mit n Elementen kann als Labyrinth aus n Punkten betrachtet werden, wobei Punkt mit Index k einseitig mit den Punkten mit k + a [ k ] und k - a [ k ] verbunden ist. Mit anderen Worten, können Sie vorwärts oder rückwärts springen genau ein [ k ] Schritte vom Punkt mit dem Index k . Punkte mit einem Index außerhalb der Grenzen des Arrays werden außerhalb des Labyrinths betrachtet.

Betrachten Sie zur Veranschaulichung das folgende Array:

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

Wenn wir uns gerade am 5. Element befinden, da das Element 4 ist, können wir 4 Schritte vorwärts zum 9. Element oder 4 Schritte rückwärts zum 1. Element springen. Wenn wir Letzteres tun, erhalten wir das Element 0, was darauf hinweist, dass keine weiteren Bewegungen möglich sind. Wenn wir das erstere machen, da das 9. Element 2 ist, können wir uns dafür entscheiden, zum 11. Element zu springen, das wiederum eine 2 ist, und dann können wir erneut zum "13. Element" springen, das außerhalb der Grenzen des ist Array und betrachtet einen Ausgang zum Labyrinth.

Wenn wir also von dem Element in der Mitte ausgehen, besteht ein Weg, das Labyrinth zu verlassen, darin, 1 Schritt zurück, 4 Schritte vorwärts, 2 Schritte vorwärts und erneut 2 Schritte vorwärts zu hüpfen, was als Array ausgedrückt werden kann [-1,4,2,2]. Alternativ können Sie es mit dem Array ausdrücken, [4,8,10,12]das den auf Null basierenden Index aller Zwischen- und Endpunkte aufzeichnet (der auf 1 basierende Index ist ebenfalls in Ordnung), oder nur mit den Zeichen [-1,1,1,1].

Es ist auch in Ordnung, das Labyrinth vom Ende mit dem niedrigen Index zu verlassen.

Die erste Notation zu verwenden und vom selben Element aus zu beginnen, [1,1,1,2,2]ist ebenfalls eine Lösung, aber nicht optimal, da es 5 statt 4 Schritte gibt.

Die Aufgabe besteht darin, den kürzesten Weg zu finden, um aus dem Array-Labyrinth herauszukommen und den Weg auszugeben. Wenn es mehr als einen optimalen Pfad gibt, können Sie einen oder alle Pfade ausgeben. Wenn es keine Lösung gibt, sollten Sie einen von Ihnen gewählten falschen Wert ausgeben, der aus einem gültigen Pfad erkennbar ist (es ist auch in Ordnung, überhaupt keine Ausgabe zu erstellen).

Der Einfachheit halber ist die Anzahl der Elemente im Array immer ungerade und wir beginnen immer mit dem Element in der Mitte.

Testfälle

Die Testfälle veranschaulichen verschiedene Ausgabeformen, auf die Sie jedoch nicht beschränkt sind.

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

Technische Daten

  • Sie können eine Funktion oder ein vollständiges Programm schreiben.

  • Das Array enthält nur nichtnegative Ganzzahlen.

  • Sie können Eingaben und Ausgaben über jedes Standardformular vornehmen . Bitte geben Sie in Ihrer Antwort an, welches Formular Sie verwenden.

  • Dies ist , die niedrigste Anzahl von Bytes gewinnt.

  • Wie üblich gelten hier Standardlücken .


Ist es in Ordnung, ein verschachteltes Array auszugeben, auch wenn die Antwort eindeutig ist? (zB für die [0,8,5,9,4,1,1,1,2,1,2]Ausgabe [[-1,4,2,2]])
Bubbler

@Bubbler Ja, Sie können verschachtelte Arrays ausgeben.
Weijun Zhou

Ist es in Ordnung, den Fluchtweg in umgekehrter Reihenfolge zurückzugeben? Also [1,1,1,-1]statt [-1,1,1,1]?
Ton Hospel

@TonHospel Ja, sag es einfach in deiner Antwort.
Weijun Zhou

Testfall 2 scheint falsch zu sein. Können Sie das erklären?
Edc65

Antworten:


3

JavaScript (ES6), 117 Byte

Gibt ein Array mit 0-indizierten Zwischen- und Endpunkten oder ein leeres Array zurück, wenn keine Lösung vorhanden ist.

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

Probieren Sie es online!

Kommentiert

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

Schale , 22 Bytes

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

Gibt eine Liste mit Zeichen oder eine leere Liste zurück, wenn keine Lösung vorhanden ist. Probieren Sie es online!

Erläuterung

Dies ist eine Brute-Force-Lösung, die Listen -1,0,1mit zunehmender Länge überprüft und die erste zurückgibt, die zu einem Sprung aus dem Array führt. Da es nur eine minimale Länge hat, enthält es keine Nullen.

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

Probieren Sie es online!

Bearbeiten:

  • Eingesparte 9 Bytes durch all(..)and s => all(..)*s, if u not in x => if{u}-x
    Ersteres nutzt boolean * list == int * listdie Set-Differenz (leeres Set ist auch falsch).

Ausgabeformat: Verschachteltes Array aller optimalen Antworten, angegeben als nullbasierte Indizes für Zwischen- und Endpunkte.

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

Der Algorithmus ist einfach BFS. Zeichnet salle möglichen iPfade mit der Länge ider Iteration auf, mit Ausnahme der bereits besuchten Indizes. Beachten Sie, dass die erweiterte Sternnotation (ab) verwendet wird, da ein wiederholter Array-Zugriff teuer ist. Ich habe festgestellt, dass eine solche Notation bei richtiger Verwendung auch einige Leerzeichen reduzieren kann.

Ich habe aus der obigen Lösung auch eine rekursive (aber längere) Version gemacht. Beides s andund or swird benötigt, sonst klappt es nicht.

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

Probieren Sie es online!


2

Haskell , 207 202 Bytes

5 Bytes gespart dank 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)]]

Probieren Sie es online!

Dies ist eine Funktion, die eine Liste von Intals Parameter verwendet und eine Liste von Pfaden zurückgibt, wobei jeder Pfad eine Liste von relativen Sprüngen ist, die ausgeführt werden, um aus dem Array herauszukommen.

Die ungolfed Version:

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

Probieren Sie es online!

Zunächst versuchte man eine rekursive Rückverfolgungssuche, weil die Verwendung mainfür die Rekursion immer Spaß macht. Am Ende konnte eine unkomplizierte nicht-rekursive Breitensuche jedoch verkleinert werden, wie es diese Version ist. Dieses Programm verwendet das Eingabearray als Befehlszeilenargumente ohne geschweifte Klammern, z. B. 0 8 5 9 4 1 1 1 2 1 2für das erste bereitgestellte Beispiel. Das Programm gibt auf stdout eine Liste von 1-indizierten , durch Kommas getrennten Array-Indizes in umgekehrter Reihenfolge aus, beginnend mit dem endgültigen, außerhalb der Grenzen liegenden / "Escape" -Index und durcharbeiten der erreichten Zwischenindizes (es gibt den nicht aus Mitte, Startindex). Das Programm gibt keine geschweiften Klammern um das Array aus und hinterlässt ein Komma, da getrenntprintfAnweisungen enthalten viele Zeichen. Die Ausgabe, die dem obigen ersten Testbeispiel entspricht, ist 13,11,9,5,beispielsweise.

Wenn es keinen Fluchtweg aus dem Array-Labyrinth gibt, gibt das Programm nichts aus.

Entgolfet und erklärt ist es unten (stark entgolfet mit einigen Änderungen für die Lesbarkeit):

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

Wie für C-Code üblich, enthält die Kompilierungsausgabe natürlich eine freundliche Wand mit Warnungen und Hinweisen.



1

Perl 5 , -a: 73 Bytes

(altes Zählen: 75 Bytes, +1für aund +1zum Ersetzen -//durch -/$/und Verwenden $`für $')

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

Geben Sie das Eingabearray als eine Zeile in STDIN ein, z 0 8 5 9 4 1 1 1 2 1 2

druckt die besuchten Positionen in umgekehrter Reihenfolge einschließlich des Startpunkts aus und stürzt dann ab

Druckt nichts, wenn es keine Lösung gibt

Probieren Sie es online!


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

Probieren Sie es online!

Nimmt das Eingabelabyrinth als Array und gibt es aus, indem der Escape-Pfad vom Ausgang zum Startpunkt (einschließlich) umgekehrt gedruckt wird. Druckt nichts, wenn es keinen Fluchtweg gibt.

Bei diesem Ansatz wird die Kartenmethode missbraucht, um ein temporäres Array zu durchlaufen, in dem der Verlauf von Pfaden gespeichert ist. Dieses Array wird ständig erweitert, wenn ein weiterer möglicher Schritt erforderlich ist.

Im Prinzip könnte ich ein weiteres Byte einsparen, indem ich return xstattdessen verwende break p x, aber das würde bedeuten, zu behaupten, dass mein falscher Wert gleich dem ganzen monströsen Müll ist, der in gespeichert ist b. Wahrscheinlich wäre das zu viel, selbst wenn man die erlaubte Flexibilität der Ausgabe bedenkt ...

Komplettlösung

->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
  }  
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.