1D Лабиринт с прыгающей решеткой


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, мы можем перейти на 4 шага вперед к 9-му элементу или 4 шага назад к 1-му элементу. Если мы сделаем последнее, мы получим элемент 0, который указывает, что дальнейшие шаги невозможны. Если мы сделаем первое, так как 9-й элемент равен 2, мы можем выбрать переход к 11-му элементу, который снова равен 2, и затем мы можем снова перейти к «13-му элементу», который выходит за пределы массив и считается выходом в лабиринт.

Поэтому, если мы начнем с элемента посередине, одним из способов выйти из лабиринта будет прыжок на 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

@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 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(..)*s, if 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записывает все возможные iдлины пути на iитерации, исключая уже посещенные индексы. Обратите внимание, что расширенная звездная нотация (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

Хаскелл , 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}}}}

Попробуйте онлайн!

Первоначально попытался рекурсивный поиск в обратном направлении, потому что с помощью main для рекурсии всегда весело. В конце концов, хотя простой и нерекурсивный поиск в ширину удалось сделать меньшим, что и является этой версией. Эта программа принимает входной массив в качестве аргументов командной строки без фигурных скобок, например, 0 8 5 9 4 1 1 1 2 1 2для первого предоставленного примера. Программа выводит на стандартный вывод список из 1 проиндексированных индексов массива , разделенных запятыми, в обратном порядке, начиная с конечного индекса out-of-bounds / 'escaped' и возвращаясь к достигнутым промежуточным индексам (она не выводит центр, начальный индекс). Программа не выводит фигурные скобки вокруг массива и оставляет запятую, потому что отдельный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;
            }
        }
    }
}

Как обычно для гольф-кода С, вывод компиляции, конечно, будет включать дружественную стену предупреждений и заметок.



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
  }  
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.