1D Hopping Array Maze


17

Terinspirasi oleh We do tower hopping dan terkait dengan 2D Maze Minus 1D

pengantar

Tugas Anda adalah menemukan jalur terpendek untuk keluar dari labirin array mengikuti aturan yang ditentukan.

Tantangan

Array 1D a dengan n elemen dapat dianggap sebagai labirin yang terdiri dari n titik, di mana titik dengan indeks k terhubung ke titik-titik dengan k + a [ k ] dan k - a [ k ] dengan cara satu arah. Dengan kata lain, Anda dapat melompat maju atau mundur persis langkah [ k ] dari titik dengan indeks k . Poin dengan indeks di luar batas array dianggap di luar labirin.

Untuk menggambarkan ini, pertimbangkan array berikut,

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

Jika kita berada di elemen ke-5 sekarang, karena elemennya adalah 4, kita dapat melompat 4 langkah ke depan ke elemen 9, atau 4 langkah mundur ke elemen 1. Jika kita melakukan yang terakhir, kita berakhir dengan elemen 0, yang menunjukkan tidak ada gerakan lebih lanjut yang mungkin. Jika kita melakukan yang pertama, karena elemen ke-9 adalah 2, kita dapat memilih untuk melompat ke elemen ke-11, yang merupakan 2, dan kemudian kita dapat melompat lagi ke "elemen ke-13", yang berada di luar batas dari array dan dianggap sebagai jalan keluar ke labirin.

Jadi jika kita mulai dari elemen di tengah, salah satu cara untuk keluar dari labirin adalah melompat 1 langkah mundur, 4 langkah maju, 2 langkah maju dan 2 langkah maju, yang dapat dinyatakan sebagai array [-1,4,2,2]. Atau Anda dapat mengekspresikannya dengan array [4,8,10,12]yang mencatat indeks berbasis nol dari semua poin perantara dan akhir (indeks berbasis 1 juga baik-baik saja), atau hanya tanda-tanda [-1,1,1,1],.

Lolos dari labirin dari ujung indeks rendah juga OK.

Menggunakan notasi pertama dan mulai dari elemen yang sama, [1,1,1,2,2]juga merupakan solusi tetapi tidak optimal karena ada 5 langkah, bukannya 4.

Tugasnya adalah untuk menemukan jalur terpendek untuk keluar dari labirin array dan menampilkan jalur. Jika ada lebih dari satu jalur optimal, Anda dapat menampilkan salah satu atau semuanya. Jika tidak ada solusi, Anda harus menampilkan nilai palsu yang dipilih oleh Anda yang dapat dilihat dari jalur yang valid (tidak menghasilkan keluaran sama sekali juga OK).

Untuk mempermudah, jumlah elemen dalam array selalu angka ganjil dan kami selalu mulai dari elemen di tengah.

Uji kasus

Kasing uji menggambarkan berbagai bentuk keluaran, tetapi Anda tidak terbatas pada ini.

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

Spesifikasi

  • Anda dapat menulis fungsi atau program lengkap.

  • Array hanya berisi bilangan bulat tidak negatif.

  • Anda dapat mengambil input dan output melalui formulir standar apa pun , tetapi harap tentukan dalam jawaban Anda formulir mana yang Anda gunakan.

  • Ini adalah , jumlah byte terendah yang menang.

  • Seperti biasa, celah default berlaku di sini.


Apakah saya tetap bisa menghasilkan array bersarang bahkan jika jawabannya unik? (misalnya untuk [0,8,5,9,4,1,1,1,2,1,2], keluaran [[-1,4,2,2]])
Bubbler

@ Lubbler Ya, Anda bisa menampilkan array bersarang.
Weijun Zhou

Apakah boleh untuk mengembalikan jalur keluar dalam urutan terbalik. Jadi, [1,1,1,-1]bukannya [-1,1,1,1]?
Ton Hospel

@TonHospel Ya, katakan saja dalam jawaban Anda.
Weijun Zhou

test case 2 tampaknya salah, bisakah Anda menjelaskannya?
edc65

Jawaban:


3

JavaScript (ES6), 117 byte

Mengembalikan array titik menengah dan akhir yang diindeks 0, atau array kosong jika tidak ada solusi.

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

Cobalah online!

Berkomentar

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

Sekam , 22 byte

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

Mengembalikan daftar tanda, atau daftar kosong jika solusi tidak ada. Cobalah online!

Penjelasan

Ini adalah solusi brute force yang memeriksa daftar dengan -1,0,1panjang yang bertambah, dan mengembalikan yang pertama yang menghasilkan lompatan keluar dari array. Karena panjangnya minimal, tidak akan berisi 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.

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]

Cobalah online!

Edit:

  • Disimpan 9 byte oleh all(..)and s => all(..)*s, if u not in x => if{u}-x
    Eksploitasi sebelumnya boolean * list == int * list, yang terakhir menggunakan perbedaan set (set kosong juga palsu).

Format output: Array bersarang dari semua jawaban optimal, diberikan sebagai indeks berbasis nol untuk poin tengah dan akhir.

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

Algoritma ini adalah BFS sederhana. smencatat semua kemungkinan ijalur-panjangi iterasi, tidak termasuk indeks yang sudah dikunjungi. Perhatikan bahwa notasi extended star digunakan (ab) karena akses array berulang mahal. Saya menemukan bahwa notasi seperti itu juga dapat mengurangi ruang kosong jika digunakan dengan benar.

Saya juga membuat versi rekursif (tetapi lebih lama) dari solusi di atas. Keduanya s anddan or sdibutuhkan, jika tidak, tidak akan berhasil.

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

Cobalah online!


2

Haskell , 207 202 byte

5 byte disimpan berkat 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)]]

Cobalah online!

Ini adalah fungsi yang mengambil daftar Intsebagai parameter dan mengembalikan daftar jalur di mana setiap jalur adalah daftar lompatan relatif yang diambil untuk keluar dari array.

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

Cobalah online!

Awalnya mencoba pencarian backtracking rekursif, karena menggunakan pernyataan mengambil banyak karakter. Output yang sesuai dengan contoh uji pertama di atas adalahmain untuk rekursi selalu menyenangkan. Pada akhirnya, meskipun pencarian pertama yang luas non-rekursif dapat dibuat lebih kecil, yang merupakan versi ini. Program ini menggunakan array input sebagai argumen baris perintah, tanpa kawat gigi, misalnya 0 8 5 9 4 1 1 1 2 1 2untuk contoh pertama yang diberikan. Output program pada stdout daftar 1-diindeks indeks array , dibatasi koma dalam urutan terbalik, mulai dari indeks akhir, di luar batas / 'lolos' dan bekerja kembali melalui indeks menengah yang dicapai (tidak menghasilkan output pusat, indeks awal). Program tidak menampilkan kawat gigi di sekitar array dan meninggalkan koma karena terpisahprintf13,11,9,5, , misalnya.

Jika tidak ada jalan keluar dari labirin array, program tidak menghasilkan apa-apa.

Degolfed dan dijelaskan di bawah ini (sangat degolfed dengan beberapa perubahan untuk dibaca):

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

Seperti biasa untuk kode C golf, keluaran kompilasi tentu saja akan mencakup dinding peringatan dan catatan yang ramah.



1

Perl 5 , -a: 73 byte

(penghitungan gaya lama: 75 byte, +1untuk adan +1untuk menggantikan -//dengan -/$/dan menggunakan $`untuk $')

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

Berikan input array sebagai satu baris pada STDIN misalnya 0 8 5 9 4 1 1 1 2 1 2

mencetak posisi yang dikunjungi dalam urutan terbalik termasuk titik awal, lalu macet

Mencetak apa pun jika tidak ada solusi

Cobalah online!


1

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

Cobalah online!

Mengambil labirin input sebagai larik, menghasilkan dengan mencetak jalur keluar secara terbalik, dari pintu keluar ke titik awal (inklusif). Mencetak apa pun jika tidak ada jalan keluar.

Pendekatan ini menyalahgunakan metode peta untuk beralih melalui larik sementara yang menyimpan sejarah jalur, yang terus-menerus diperluas dengan cepat setiap kali ada langkah lain yang mungkin diambil.

Pada prinsipnya, saya bisa menyimpan byte lain dengan menggunakan return xalih-alih break p x, tetapi itu berarti mengklaim bahwa nilai kepalsuan saya sama dengan semua sampah mengerikan yang disimpan di b. Mungkin, ini akan terlalu banyak, bahkan mengingat fleksibilitas yang diizinkan dari output ...

Panduan

->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
  }  
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.