一维跳跃阵列迷宫


17

我们启发,我们跳塔,并且与2D Maze Minus 1D相关

介绍

您的任务是找到遵循指定规则的最短路径,以摆脱阵列迷宫。

挑战

具有n个元素的一维数组a可以看作是由n个点组成的迷宫,其中索引k的点与k + a [ k ]和k - a [ k ]的点以单向方式连接。换句话说,可以向前或向后跳恰好一个 [ ķ从与索引点]步骤ķ。索引超出数组范围的点被视为迷宫之外。

为了说明这一点,请考虑以下数组,

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

如果我们现在位于第5个元素,则由于元素为4,我们可以将第4步向前跳到第9个元素,或者将第4个步向后跳到第一个元素。如果执行后者,我们将以元素0结束,这表明不可能进一步移动。如果执行前者,由于第9个元素为2,因此我们可以选择跳到第11个元素,它又是2,然后我们可以再次跳至“第13个元素”,这超出了阵列,并认为是迷宫的出口。

因此,如果我们从中间的元素开始,一种摆脱迷宫的方法是向后跳1步,向前4步,向前2步,再次向前2步,这可以表示为array [-1,4,2,2]。另外,您也可以使用数组来表示它,该数组[4,8,10,12]记录所有中间点和最终点的从零开始的索引(也可以从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是的,您可以输出嵌套数组。

是否可以以相反的顺序返回转义路径。所以[1,1,1,-1]代替[-1,1,1,1]
Ton Hospel '18

@TonHospel是的,只需在您的回答中这么说即可。

测试用例2似乎不正确,您能解释一下吗?
edc65

Answers:


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 3中195 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,后者使用集合差异(空集合也虚假)。

输出格式:所有最佳答案的嵌套数组,以中间点和最终点的从零开始的索引给出。

例如: f([0,8,5,9,4,1,1,1,2,1,2]) == [[4, 8, 10, 12]]

该算法是简单的BFS。s记录所有可能ii迭代中长度路径,但不包括已访问的索引。注意,因为重复的数组访问很昂贵,所以使用了扩展星号。我发现,如果使用正确,这种表示法还可以减少一些空白。

我还根据上述解决方案制作了一个递归(但更长)的版本。都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

哈斯克尔207) 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,

如果没有来自阵列迷宫的逃生路线,则程序不会输出任何内容。

在下面进行了Degolf的解释,并对其进行了解释(为便于阅读,对Degolf进行了一些更改):

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个字节,+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

红宝石,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
  }  
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.