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 Так, ви можете вивести вкладений масив.
Вейджун Чжоу

Чи правильно повернути шлях втечі у зворотному порядку. Так [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

Пітон 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 потрібно, інакше це не працює.

Пітон 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}}}}

Спробуйте в Інтернеті!

Спочатку спробували рекурсивний пошук зворотного відстеження, тому що використовувати 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, вихід компіляції, звичайно, буде містити дружню стінку попереджень та приміток.



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

Спробуйте в Інтернеті!

Приймає вхідний лабіринт як масив, виводить, друкуючи шлях відходу в зворотному напрямку, від виходу до початкової точки (включно). Нічого не друкується, якщо немає втечі.

Цей підхід зловживає методом карти для повторення тимчасового масиву, що зберігає історію шляхів, який постійно розширюється на ходу, коли є ще один можливий крок.

В принципі, я міг би зберегти ще один байт, використовуючи 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.