1D Hopping Array Maze


17

แรงบันดาลใจจากเราทำทาวเวอร์กระโดดและเกี่ยวข้องกับ2D Maze Minus 1D

บทนำ

งานของคุณคือการหาเส้นทางที่สั้นที่สุดที่จะออกจากเขาวงกตอาร์เรย์ตามกฎที่ระบุ

ท้าทาย

อาร์เรย์ 1D a ที่มีองค์ประกอบnถือได้ว่าเป็นเขาวงกตที่ประกอบด้วยจุดnซึ่งจุดที่มีดัชนีkเชื่อมต่อกับจุดที่มีk + a [ k ] และk - a [ k ] ในลักษณะทางเดียว ในคำอื่น ๆ คุณสามารถกระโดดไปข้างหน้าหรือข้างหลังตรง[ k ] ก้าวจากจุดที่มีค่าดัชนีk คะแนนที่มีดัชนีนอกขอบเขตของอาร์เรย์จะถูกพิจารณานอกเขาวงกต

เพื่ออธิบายสิ่งนี้ให้พิจารณาอาร์เรย์ต่อไปนี้

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

หากเราอยู่ที่องค์ประกอบที่ 5 ในขณะนี้เนื่องจากองค์ประกอบคือ 4 เราสามารถข้ามไปข้างหน้าไปยังองค์ประกอบที่ 9 หรือ 4 ขั้นตอนย้อนหลังไปยังองค์ประกอบที่ 1 ได้ ถ้าเราทำอย่างหลังเราจะจบลงด้วยองค์ประกอบ 0 ซึ่งบ่งชี้ว่าไม่มีการเคลื่อนไหวเพิ่มเติม ถ้าเราทำแบบเดิมเนื่องจากองค์ประกอบที่ 9 คือ 2 เราสามารถเลือกที่จะกระโดดไปที่องค์ประกอบที่ 11 ซึ่งเป็นอีก 2 และจากนั้นเราสามารถกระโดดอีกครั้งเพื่อ "องค์ประกอบที่ 13" ซึ่งอยู่นอกขอบเขตของ อาร์เรย์และพิจารณาทางออกให้กับเขาวงกต

ดังนั้นถ้าเราเริ่มต้นจากองค์ประกอบที่อยู่ตรงกลางวิธีหนึ่งที่จะได้รับออกจากเขาวงกตที่มีการกระโดดที่ 1 กลับไปขั้นตอนที่ 4 ขั้นตอนข้างหน้าขั้นตอนที่ 2 ไปข้างหน้าและอีก 2 [-1,4,2,2]ขั้นตอนข้างหน้าซึ่งสามารถแสดงเป็นอาร์เรย์ หรือคุณสามารถแสดงมันมีอาร์เรย์[4,8,10,12]ที่บันทึกดัชนี zero-based ของจุดกลางและขั้นสุดท้ายทั้งหมด (ดัชนี 1 ที่ใช้เป็นยังดี) [-1,1,1,1]หรือเพียงแค่สัญญาณ

การหลบหนีจากเขาวงกตจากจุดสิ้นสุดดัชนีต่ำก็ถือว่าใช้ได้เช่นกัน

การใช้สัญกรณ์แรกและเริ่มต้นจากองค์ประกอบเดียวกัน[1,1,1,2,2]ก็เป็นวิธีแก้ปัญหาเช่นกัน แต่ก็ไม่เหมาะสมเนื่องจากมี 5 ขั้นตอนแทนที่จะเป็น 4

ภารกิจคือการหาเส้นทางที่สั้นที่สุดเพื่อออกจากเขาวงกตอาร์เรย์และออกเส้นทาง หากมีเส้นทางที่ดีที่สุดมากกว่าหนึ่งเส้นทางคุณสามารถส่งออกเส้นทางใดเส้นทางหนึ่งหรือทั้งหมดได้ หากไม่มีวิธีแก้ปัญหาคุณควรส่งออกค่าที่ผิดพลาดที่คุณเลือกซึ่งมองเห็นได้จากเส้นทางที่ถูกต้อง

เพื่อความง่ายจำนวนขององค์ประกอบในอาเรย์จะเป็นจำนวนคี่เสมอและเรามักจะเริ่มจากองค์ประกอบที่อยู่ตรงกลาง

กรณีทดสอบ

กรณีทดสอบแสดงให้เห็นถึงรูปแบบต่าง ๆ ของการส่งออก แต่คุณไม่ จำกัด เหล่านี้

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

รายละเอียด


มันจะดีกว่าที่จะส่งออกอาร์เรย์ที่ซ้อนกันแม้ว่าคำตอบจะไม่ซ้ำกัน? (เช่นสำหรับการ[0,8,5,9,4,1,1,1,2,1,2]แสดงผล[[-1,4,2,2]])
Bubbler

@Bubbler ใช่คุณสามารถส่งออกอาร์เรย์ที่ซ้อนกัน
Weijun Zhou

ตกลงหรือไม่ที่จะส่งคืนเส้นทางการหลบหนีในลำดับย้อนกลับ ดังนั้น[1,1,1,-1]แทนที่จะ[-1,1,1,1]?
Ton Hospel

@ TonHospel ใช่แค่พูดอย่างนั้นในคำตอบของคุณ
Weijun Zhou

กรณีทดสอบ 2 ดูเหมือนไม่ถูกต้องคุณสามารถอธิบายได้ไหม
edc65

คำตอบ:


3

JavaScript (ES6), 117 ไบต์

ส่งคืนอาร์เรย์ของคะแนนกลางและสุดท้าย 0 ที่ทำดัชนีหรืออาร์เรย์ว่างถ้าไม่มีวิธีแก้ไข

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

ลองออนไลน์!

แสดงความคิดเห็น

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 ไบต์

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

ส่งคืนรายการสัญญาณหรือรายการว่างเปล่าหากไม่มีวิธีแก้ไข ลองออนไลน์!

คำอธิบาย

นี่เป็นวิธีการแก้ปัญหาแบบ brute-force ที่ตรวจสอบรายการมากกว่า-1,0,1ความยาวที่เพิ่มขึ้นและส่งกลับรายการแรกที่ส่งผลให้เกิดการกระโดดออกจากอาร์เรย์ เนื่องจากมันมีความยาวน้อยที่สุดจึงไม่มี 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 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]

ลองออนไลน์!

แก้ไข:

  • ที่บันทึกไว้ 9 ไบต์โดยall(..)and s => all(..)*s, if u not in x => if{u}-x
    อดีตหาประโยชน์boolean * list == int * listที่แตกต่างการใช้งานหลังการตั้งค่า (ชุดที่ว่างเปล่ายังเป็น falsy)

รูปแบบผลลัพธ์: อาร์เรย์ที่ซ้อนกันของคำตอบที่ดีที่สุดทั้งหมดให้เป็นดัชนีที่เป็นศูนย์ของคะแนนกลางและสุดท้าย

ตัวอย่างเช่นf([0,8,5,9,4,1,1,1,2,1,2]) == [[4, 8, 10, 12]].

อัลกอริทึมเป็น BFS ง่าย sบันทึกiเส้นทางความยาวที่เป็นไปได้ทั้งหมดในiการทำซ้ำ th ไม่รวมดัชนีที่เข้าชมแล้ว โปรดทราบว่าใช้เครื่องหมายสัญกรณ์แบบขยาย (ab) เนื่องจากการเข้าถึงอาร์เรย์ซ้ำมีราคาแพง ฉันพบว่าสัญกรณ์ดังกล่าวยังสามารถลดช่องว่างบางอย่างถ้าใช้อย่างถูกต้อง

ฉันยังทำรุ่นเรียกซ้ำ (แต่ยาวกว่า) ออกจากโซลูชันด้านบน ทั้งสองs andและor sมีความจำเป็นมิฉะนั้นมันจะไม่ทำงาน

Python 3 , 210 ไบต์

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

ลองออนไลน์!


2

Haskell , 207 202 ไบต์

5 ไบต์บันทึกขอบคุณที่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)]]

ลองออนไลน์!

นี่คือฟังก์ชั่นที่รับรายการIntเป็นพารามิเตอร์และส่งคืนรายการพา ธ ที่แต่ละพา ธ เป็นรายการของการข้ามแบบสัมพันธ์ที่ใช้เพื่อออกจากอาร์เรย์

เวอร์ชันที่ไม่ดีงาม:

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 ไบต์

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

ลองออนไลน์!

เริ่มแรกลองค้นหา backtracking แบบเรียกซ้ำเนื่องจากการใช้mainการเรียกซ้ำเป็นเรื่องสนุกเสมอ ในท้ายที่สุดถึงแม้ว่าการค้นหาแบบกว้างแรกที่ไม่ต้องเรียกซ้ำแบบตรงไปตรงมาก็สามารถทำให้มีขนาดเล็กลงซึ่งเป็นสิ่งที่รุ่นนี้เป็น โปรแกรมนี้ใช้อาร์เรย์อินพุตเป็นอาร์กิวเมนต์บรรทัดคำสั่งโดยไม่มีวงเล็บปีกกาเช่น 0 8 5 9 4 1 1 1 2 1 2สำหรับตัวอย่างแรกที่มีให้ โปรแกรมเอาต์พุตเมื่อ stdout รายการคำสั่งที่มีดัชนี 1ดัชนีอาเรย์ที่มีดัชนีคั่นด้วยเครื่องหมายจุลภาคในลำดับย้อนกลับเริ่มจากดัชนีสุดท้ายนอกขอบเขต / 'หนี' และกลับมาทำงานผ่านดัชนีระดับกลางถึง (ไม่ส่งออก กลาง, ดัชนีเริ่มต้น) โปรแกรมจะไม่แสดงเครื่องหมายปีกการอบ ๆ อาร์เรย์และปล่อยเครื่องหมายจุลภาคต่อท้ายเนื่องจากแยกออกจากกันรายการprintfมีอักขระจำนวนมาก ผลผลิตที่สอดคล้องกับตัวอย่างการทดสอบครั้งแรกข้างต้นเป็น13,11,9,5,ตัวอย่างเช่น

หากไม่มีเส้นทางหลบหนีจากเขาวงกตอาร์เรย์โปรแกรมจะไม่แสดงผลใด ๆ

ทำให้แย่ลงและอธิบายว่ามันอยู่ด้านล่าง (ถูกลดทอนอย่างหนักกับการเปลี่ยนแปลงบางอย่างสำหรับการอ่าน):

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

ตามปกติสำหรับรหัส C golfed ผลลัพธ์ของการรวบรวมจะรวมถึงกำแพงคำเตือนและบันทึกย่อที่เป็นมิตร



1

Perl 5 , -a: 73 ไบต์

(การนับรูปแบบเก่า: 75 ไบต์+1สำหรับaและ+1เปลี่ยน-//โดย-/$/และใช้$`สำหรับ$')

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

กำหนดให้อาร์เรย์อินพุตเป็นหนึ่งบรรทัดบน STDIN เช่น 0 8 5 9 4 1 1 1 2 1 2

พิมพ์ตำแหน่งที่เยี่ยมชมในลำดับย้อนกลับรวมถึงจุดเริ่มต้นแล้วเกิดปัญหา

พิมพ์อะไรถ้าไม่มีวิธีแก้ปัญหา

ลองออนไลน์!


1

Ruby , 102 ไบต์

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

ลองออนไลน์!

รับอินพุตเขาวงกตเป็นอาร์เรย์เอาต์พุตโดยพิมพ์เส้นทางหนีกลับด้านจากทางออกไปยังจุดเริ่มต้น (รวม) พิมพ์อะไรเลยถ้าไม่มีทางหนี

วิธีนี้ใช้วิธีแผนที่ในการวนซ้ำผ่านอาร์เรย์ชั่วคราวที่เก็บประวัติของเส้นทางซึ่งจะขยายอย่างต่อเนื่องทันทีเมื่อใดก็ตามที่มีขั้นตอนอื่นที่เป็นไปได้

โดยหลักการแล้วฉันสามารถบันทึกไบต์อื่นได้โดยใช้return xแทนbreak p xแต่นั่นหมายถึงการอ้างว่าค่าความเท็จของฉันเท่ากับขยะมหึมาทั้งหมดที่เก็บไว้ในbของฉันคือเท่ากับทั้งหมดขยะมหึมาที่เก็บไว้ใน อาจเป็นไปได้ว่านี่จะมากเกินไปแม้จะพิจารณาถึงความยืดหยุ่นที่อนุญาตของเอาต์พุต ...

เกมส์

->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
  }  
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.