1Dホッピング配列迷路


17

触発さ我々の塔は、ホッピングんとに関連した2D迷路マイナス1D

前書き

あなたの仕事は、指定されたルールに従って配列迷路から抜け出すための最短経路を見つけることです。

チャレンジ

n個の要素を持つ1D配列aはn 個の点で構成される迷路と見なすことができます。インデックスkの点は、一方向にk + a [ k ]およびk - a [ k ] の点に接続されます。つまり、インデックスkのポイントからa [ k ]ステップだけ正確に前後にジャンプできます。配列の境界外のインデックスを持つポイントは、迷路外と見なされます。

これを説明するために、次の配列を検討してください。

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

現在5番目の要素にいる場合、要素は4であるため、9番目の要素まで4ステップ進むか、1番目の要素まで4ステップ戻ることができます。後者の場合、要素0になります。これは、それ以上移動できないことを示します。前者を実行すると、9番目の要素が2であるため、11番目の要素(再び2)にホップすることを選択できます。その後、「13番目の要素」に再びホップできます。配列し、迷路への出口と見なされます。

したがって、真ん中の要素から開始する場合、迷路から抜け出すための1つの方法は、1ステップ戻る、4ステップ進む、2ステップ進む、そして再び2ステップ進むこと[-1,4,2,2]です。これは配列として表すことができます。または[4,8,10,12]、すべての中間点と最終点のゼロベースのインデックスを記録する配列(1ベースのインデックスでも問題ありません)、または単に記号で表現できます[-1,1,1,1]

低指数の端から迷路を脱出することも大丈夫です。

最初の表記を使用して同じ要素から開始すること[1,1,1,2,2]も解決策ですが、4つではなく5つのステップがあるため最適ではありません。

タスクは、配列の迷路から抜け出すための最短パスを見つけ出し、パスを出力することです。最適なパスが複数ある場合は、それらの一部またはすべてを出力できます。解決策がない場合は、有効なパスから識別可能な偽の値を選択して出力する必要があります(出力をまったく生成しなくてもかまいません)。

簡単にするために、配列内の要素の数は常に奇数であり、常に真ん中の要素から始めます。

テストケース

テストケースはさまざまな形式の出力を示していますが、これらに限定されません。

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はい、ネストされた配列を出力できます。
ウェイジュン周

エスケープパスを逆の順序で返すことは可能ですか?では[1,1,1,-1]なく[-1,1,1,1]
トンホスペル

@TonHospelはい、答えでそう言ってください。
ウェイジュン周

テストケース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

、22バイト

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

記号のリスト、またはソリューションが存在しない場合は空のリストを返します。 オンラインでお試しください!

説明

これは、-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の3195の 188 179バイト

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(..)*sif 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反復で記録します。配列への繰り返しアクセスにはコストがかかるため、拡張スター表記法は(ab)使用されることに注意してください。このような表記法を正しく使用すると、空白を減らすことができることがわかりました。

また、上記のソリューションから再帰的な(しかしより長い)バージョンを作成しました。両方s andor 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

Haskell207 202バイト

BMOのおかげで5バイト節約されました。

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

オンラインでお試しください!

main再帰の使用は常に楽しいため、最初は再帰的なバックトラッキング検索を試みました。最終的には、単純な非再帰的な幅優先検索を小さくすることができました。これがこのバージョンです。このプログラムは、0 8 5 9 4 1 1 1 2 1 2最初の例のように、入力配列をブレースなしでコマンドライン引数として受け取ります 。プログラムは、stdoutに1インデックス付きのコンマ区切りの配列インデックスのリストを逆順で出力します。最後の範囲外/「エスケープ」インデックスから始まり、到達した中間インデックスに戻ります(出力されません)センター、開始インデックス)。プログラムは配列の周りに中括弧を出力せず、末尾にコンマを残しますprintf文には多くの文字が必要です。上記の最初のテスト例に対応する出力は13,11,9,5,、たとえばです。

配列迷路からのエスケープルートがない場合、プログラムは何も出力しません。

Degolfedは、それが以下にあることを説明しました(読みやすさのためにいくつかの変更を加えて大きくdegolfed)

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コードの場合と同様に、コンパイル出力にはもちろん、警告とメモのわかりやすい壁が含まれます。



1

Perl 5、-a:73バイト

(古いスタイルのカウント:75バイト、+1a+1交換する-//ことにより、-/$/および使用$`のため$'

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

入力配列をSTDIN上の1行として与える 0 8 5 9 4 1 1 1 2 1 2

訪問した位置を開始点を含む逆順で印刷し、クラッシュします

解決策がない場合は何も印刷しません

オンラインでお試しください!


1

ルビー、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)}}

オンラインでお試しください!

入力迷路を配列として取り、出口から開始点までのエスケープパスを逆方向に出力して出力します(両端を含む)。エスケープがない場合は何も出力しません。

このアプローチは、mapメソッドを誤って使用して、パスの履歴を格納する一時配列を反復処理します。これは、実行可能な別のステップがある場合は常にオンザフライで拡張されます。

原則として、の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.