Оптимальний шлях через матрицю


19

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

Приклад:

 1*   9    7    3   10    2    2
10    4*   1*   1*   1*   7    8
 3    6    3    8    9    5*   7
 8   10    2    5    2    1*   4
 5    1    1    3    6    7    9*

Шлях, що дає найменшу суму, позначається зірочками, і виходить така сума: 1 + 4 + 1 + 1 + 1 + 5 + 1 + 9 = 23 .

Тестові приклади:

1   1   1
1   1   1
Output: 3

 7    9    6    6    4
 6    5    9    1    6
10    7   10    4    3
 4    2    2    3    7
 9    2    7    9    4
Output: 28

2  42   6   4   1
3  33   1   1   1
4  21   7  59   1
1   7   6  49   1
1   9   2  39   1
Output: 27 (2+3+4+7+7+1+1+1+1)

 5    6    7    4    4
12   12   25   25   25
 9    4   25    9    5
 7    4   25    1   12
 4    4    4    4    4
Output: 34 (5+12+4+4+4+1+4)

1   1   1   1
9   9   9   1
1   9   9   9
1   9   9   9
1   1   1   1
Output: 15

 2   55    5    3    1    1    4    1
 2   56    1   99   99   99   99    5
 3   57    5    2    2    2   99    1
 3   58    4    2    8    1   99    2
 4   65   66   67   68    3   99    3
 2    5    4    3    3    4   99    5
75   76   77   78   79   80   81    2
 5    4    5    1    1    3    3    2
Output: 67 (2+2+3+3+4+5+4+3+3+3+1+2+2+1+3+1+1+4+5+1+2+3+5+2+2)

Це тому найкоротший код на кожній мові виграє.


Дуже схожий , хоча не дозволяє рух по діагоналі.
Мего

7
@WheatWizard Я не згоден. Крім здебільшого поверхневих відмінностей, що цей виклик дозволяє здійснювати рух по діагоналі, і всі позиції доступні, інший виклик вимагає повернення самого шляху, а не просто вартості шляху. Якщо ви не використовуєте вбудовані модулі, які повертають обидва, код не є взаємозамінним.
стакан

@beaker Я не бачу різниці. Ви повинні знайти шлях, щоб знати його довжину. На мою думку, різниця тут є досить невеликою різницею у виробництві, і цей виклик не пропонує нічого нового чи цікавого, що вже не охоплене цим викликом.
Пшеничний майстер

1
@WheatWizard Моє рішення тут не знаходить шляху. Він міг би знайти шлях, але не без окремого масиву попередників та логіки, щоб уникнути створення вузла своїм попередником. Не кажучи вже про відновлення шляху в кінці.
стакан

@beaker Модифікація є досить тривіальною на мою думку. Незважаючи на те, що граф не полягає в тому, чи можна кожний дійсний запис на один виклик переносити з мінімальними зусиллями, це стосується загальної справи. Я не тільки думаю, що більшість зусиль тут можна перенести, я не думаю, що цей виклик пропонує щось нове або цікаве від інших.
Пшеничний майстер

Відповіді:


8

JavaScript, 442 412 408 358 байт

Це моє перше подання про PPCG. Відгуки будуть вдячні.

(m,h=m.length,w=m[0].length)=>{for(i=0;i<h*w;i++)for(x=0;x<w;x++){for(y=0;y<h;y++){if(m[y][x]%1==0)m[y][x]={c:m[y][x],t:m[y][x]};for(X=-1;X<=1;X++)for(Y=-1;Y<=1;Y++){t=x+X;v=y+Y;if((X==0&&Y==0)||t<0||t>=w||v<0||v>=h)continue;if(m[v][t]%1==0)m[v][t]={c:m[v][t],t:null};c=m[y][x].t+m[v][t].c;if (c<m[v][t].t||m[v][t].t==null)m[v][t].t=c}}}return m[h-1][w-1].t}

Для цього використовується багатовимірний масив як вхідний.

Пояснення

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

Демо

f=(m,h=m.length,w=m[0].length)=>{for(i=0;i<h*w;i++)for(x=0;x<w;x++){for(y=0;y<h;y++){if(m[y][x]%1==0)m[y][x]={c:m[y][x],t:m[y][x]};for(X=-1;X<=1;X++)for(Y=-1;Y<=1;Y++){t=x+X;v=y+Y;if((X==0&&Y==0)||t<0||t>=w||v<0||v>=h)continue;if(m[v][t]%1==0)m[v][t]={c:m[v][t],t:null};c=m[y][x].t+m[v][t].c;if (c<m[v][t].t||m[v][t].t==null)m[v][t].t=c}}}return m[h-1][w-1].t}

//Tests
console.log(f([[1,1,1],[1,1,1]])===3);
console.log(f([[7,9,6,6,4],[6,5,9,1,6],[10,7,10,4,3],[4,2,2,3,7],[9,2,7,9,4]])===28);
console.log(f([[2,42,6,4,1],[3,33,1,1,1],[4,21,7,59,1],[1,7,6,49,1],[1,9,2,39,1]])===27);
console.log(f([[5,6,7,4,4],[12,12,25,25,25],[9,4,25,9,5],[7,4,25,1,12],[4,4,4,4,4]])===34); 
console.log(f([[1,1,1,1],[9,9,9,1],[1,9,9,9],[1,9,9,9],[1,1,1,1]])===15)
console.log(f([[2,55,5,3,1,1,4,1],[2,56,1,99,99,99,99,5],[3,57,5,2,2,2,99,1],[3,58,4,2,8,1,99,2],[4,65,66,67,68,3,99,3],[2,5,4,3,3,4,99,5],[75,76,77,78,79,80,81,2],[5,4,5,1,1,3,3,2]])===67);

Редагувати: Особлива подяка @ETHproductions за те, що мені допомогли поголити десятки смачних байтів.

Ще дякую @Stewie Griffin за ваші поради, які збили 50 байт.


3
Ласкаво просимо до PPCG! Є кілька додаткових пробілів, які ви можете видалити до кінця (я рахую 5 усього), і вам не потрібен жоден крапки з комою безпосередньо перед цим, }який повинен зберегти кілька байт. Вам також не потрібно декларувати свої змінні; Я думаю, що видалення vars допоможе вам заощадити ще 24 байти.
ETHproductions

2
Ласкаво просимо до PPCG =) Я радий, що ви вибрали одне з моїх викликів як вихідний. Мій єдиний коментар: я люблю пояснення. Однак це необов’язково, тому вам не доведеться додавати його, якщо не захочете. :)
Стюі Гріффін

Я думаю, можливо, зберігання m[v][t]як змінної: t=x+X;v=y+Y;k=m[v][t]буде ще коротшим ...?
Стюі Гріффін


6

Пакет для обробки зображень Octave +, 175 162 157 151 142 139 байт

Збережено 14 байт завдяки @Luis Mendo та 1 байт завдяки @notjagan

function P(G)A=inf(z=size(G));A(1)=G(1);for k=G(:)'B=im2col(padarray(A,[1,1],inf),[3,3])+G(:)';B(5,:)-=G(:)';A=reshape(min(B),z);end,A(end)

Використовує пакет для обробки зображень, бо чому б ні? Чи не так усі вирішують задачі графіків?

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

Вибухнув

function P(G)
   A=inf(z=size(G));         % Initialize distance array to all Inf
   A(1)=G(1);                % Make A(1) = cost of start cell
   for k=G(:)'               % For a really long time...
      B=im2col(padarray(A,[1,1],inf),[3,3])+G(:)';
       %  B=padarray(A,[1,1],inf);     % Add border of Inf around distance array
       %  B=im2col(B,[3,3]);           % Turn each 3x3 neighborhood into a column
       %  B=B+G(:)';                   % Add the weights to each row
      B(5,:)-=G(:)';         % Subtract the weights from center of neighborhood
      A=reshape(min(B),z);   % Take minimum columnwise and reshape to original
   end
   A(end)                    % Display cost of getting to last cell

Пояснення

Дано масив ваг:

7   12    6    2    4
5   13    3   11    1
4    7    2    9    3
4    2   12   13    4
9    2    7    9    4

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

  7   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf

Це ітерація 0. Для кожної наступної ітерації вартість досягнення клітини встановлюється як мінімум:

  • поточні витрати на досягнення цього елемента, і
  • поточна вартість для досягнення сусідів елемента + вага елемента

Після першої ітерації вартість шляху до елемента (2,2) (з використанням індексації на основі 1) становитиме

minimum([  7   Inf   Inf]   [13  13  13]) = 20
        [Inf   Inf   Inf] + [13   0  13]
        [Inf   Inf   Inf]   [13  13  13]

Повний масив витрат після першої ітерації буде:

  7    19   Inf   Inf   Inf
 12    20   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf

Після ітерації kкожен елемент матиме найменшу вартість досягнення цього елемента з самого початку, зробивши не більшість kкроків. Наприклад, елемент (3,3) може бути досягнутий у 2 етапи (ітерації) вартістю 22:

  7    19    25   Inf   Inf
 12    20    22   Inf   Inf
 16    19    22   Inf   Inf
Inf   Inf   Inf   Inf   Inf
Inf   Inf   Inf   Inf   Inf

Але на 4-й ітерації знайдений шлях у 4 кроки вартістю 20:

 7   19   25   24   28
12   20   22   32   25
16   19   20   30   34
20   18   30   34   35
27   20   25   40   39

Оскільки жоден шлях через матрицю mxn не може перевищувати кількість елементів у матриці (як дуже вільна верхня межа), після m*nітерацій кожен елемент буде містити вартість найкоротшого шляху, щоб досягти цього елемента з самого початку.


-1 байт, видаливши пробіл між whileі ~.
notjagan

@notjagan я перейшов з whileдо forі все ще в змозі використати свій відгук. Спасибі!
стакан

5

JavaScript, 197 байт

a=>(v=a.map(x=>x.map(_=>1/0)),v[0][0]=a[0][0],q=[...(a+'')].map(_=>v=v.map((l,y)=>l.map((c,x)=>Math.min(c,...[...'012345678'].map(c=>a[y][x]+((v[y+(c/3|0)-1]||[])[x+c%3-1]||1/0)))))),v.pop().pop())

Досить:

a=>(
  // v is a matrix holds minimal distance to the left top
  v=a.map(x=>x.map(_=>1/0)),
  v[0][0]=a[0][0],
  q=[
     // iterate more than width * height times to ensure the answer is correct
    ...(a+'')
  ].map(_=>
    v=v.map((l,y)=>
      l.map((c,x)=>
        // update each cell
        Math.min(c,...[...'012345678'].map(
          c=>a[y][x]+((v[y+(c/3|0)-1]||[])[x+c%3-1]||1/0)
        ))
      )
    )
  ),
  // get result at right bottom
  v.pop().pop()
)

4

Математика 279 байт

Основна ідея полягає у створенні графіка з вершинами, відповідними записам матриці та спрямованими ребрами між будь-якими двома вершинами, розділеними ChessboardDistanceбільшим за нуль, але меншим або рівним 1. Між іншим, це трапляється відомим як графік Кінга , оскільки він відповідає дійсні ходи короля на шаховій дошці.

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

Код:

(m=Flatten[#];d=Dimensions@#;s=Range[Times@@d];e=Select[Tuples[s,2],0<ChessboardDistance@@(#/.Thread[s->({Ceiling[#/d[[1]]],Mod[#,d[[1]],1]}&/@s)])≤1&];Tr[FindShortestPath[Graph[s,#[[1]]->#[[2]]&/@e,EdgeWeight->(Last@#&/@Map[Extract[m,#]&,e,{2}])],1,Last@s]/.Thread[s->m]])&

Зауважте, що символ є символом транспонування. Він вставиться в Mathematica як є.

Використання:

%@{{2, 55, 5, 3, 1, 1, 4, 1},
  {2, 56, 1, 99, 99, 99, 99, 5},
  {3, 57, 5, 2, 2, 2, 99, 1},
  {3, 58, 4, 2, 8, 1, 99, 2},
  {4, 65, 66, 67, 68, 3, 99, 3},
  {2, 5, 4, 3, 3, 4, 99, 5},
  {75, 76, 77, 78, 79, 80, 81, 2},
  {5, 4, 5, 1, 1, 3, 3, 2}}

Вихід:

67

Якщо встановити g=Graph[...,GraphLayout->{"GridEmbedding","Dimension"->d},VertexLabels->Thread[s->m]і p=FindShortestPath[...тоді наступний графік буде візуально відображати рішення ( в верхній частині матриці відповідає нижній частині графіка):

HighlightGraph[g,PathGraph[p,Thread[Most@p->Rest@p]]]

введіть тут опис зображення


3

Haskell, 228 байт

Позиції - це списки з двох елементів, тому що їх легко генерувати sequenceі так само легко узгоджувати, як і 2-кортежі.

h=g[[-1,-1]]
g t@(p:r)c|p==m=0|1<2=minimum$(sum$concat c):(\q@[a,b]->c!!a!!b+g(q:t)c)#(f(e$s$(\x->[0..x])#m)$f(not.e t)$zipWith(+)p#s[[-1..1],[-1..1]])where m=[l(c)-1,l(head c)-1]
(#)=map
f=filter
e=flip elem
s=sequence
l=length

Почніть з -1,-1 і рахуйте вартість кожного пункту призначення пункту.

Альтернативно перші два рядки: починати з 0,0, рахувати поля відправлення, закінчувати на координатах, рівних розмірам матриці (так вниз направо від мети, яку потрібно додати до списку законних напрямків) - точно такої ж довжини, але повільніше:

j=i[[0,0]]
i t@(p@[a,b]:r)c|p==m=0|1<2=c!!a!!b+(minimum$(sum$concat c):(\q->i(q:t)c)#(f(e$m:(s$(\x->[0..x-1])#m))$f(not.e t)$zipWith(+)p#s[[-1..1],[-1..1]]))where m=[l c,l$head c]

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

Вдосконалюється: Надлишки filters. Злиття / в-вирівнюючи їх filter(flip elem$(s$(\x->[0..x])#m)\\p)з import Data.Listна \\витрати 3 байта.

Також занадто погано (fromEnumTo 0)на 2 байти довше, ніж (\x->[0..x]).

sum$concat c- підсумована вартість всіх полів і, таким чином, коротко виражається верхня межа вартості шляху, яка надається для minimumуникнення порожнього списку (моя перевірка типу вже визначила всю роботу над Integers, тому немає жорсткого кодування максимуму , хе-хе). Як би я не обмежував кроки на основі попереднього зробленого (який би значно прискорив алгоритм, але і коштував байти), я не можу уникнути тупикових ситуацій, які роблять цю резервну необхідність.

  • Одна ідея фільтра була ((not.e n).zipWith(-)(head r))з вилученням n=s[[-1..1],[-1..1]], що вимагає додавання ,[-1,-1]до початкового шляху. Тоді алгоритм дозволяє уникнути того, куди він міг би вже піти на попередньому кроці, що робить перехід на крайове поле ортогонально до цього краю в глухий кут.

  • Інший був ((>=0).sum.z(*)d)з вилученням z=zipWith, яке вводить новий аргумент dдо рекурсивної функції, яка задається як (z(-)p q)у рекурсії, так і [1,1]в початковому випадку. Алгоритм дозволяє уникнути послідовних кроків з негативним скалярним продуктом ( dякий був попереднім кроком), що означає відсутність різких 45-градусних поворотів. Це все ще суттєво звужує вибір і уникає попередньої тривіальної тупикової точки, але все ж є шляхи, які опиняються вкладеними у вже відвідані поля (і, можливо, "втеча", яка, однак, стане різким поворотом).


3

Python 2, 356 320 байт

s=input()
r=lambda x:[x-1,x,x+1][-x-2:]
w=lambda z:[z+[(x,y)]for x in r(z[-1][0])for y in r(z[-1][1])if x<len(s)>0==((x,y)in z)<len(s[0])>y]
l=len(s)-1,len(s[0])-1
f=lambda x:all(l in y for y in x)and x or f([a for b in[l in z and[z]or w(z)for z in x]for a in b])
print min(sum(s[a][b]for(a,b)in x)for x in f([[(0,0)]]))

Спробуйте тут!

-36 байт завдяки notjagan !

Отримує список списків у якості вхідних даних і видає найнижчу вартість під час навігації по матриці зліва вліво внизу праворуч.

Пояснення

Знайдіть кожен можливий маршрут з верхнього лівого до нижнього правого краю матриці, створивши список координат x, y для кожного маршруту. Маршрути не можуть бути відхиленими, і вони повинні закінчуватися о(len(s)-1,len(s[0])-1) .

Підсумовуйте цілі числа по кожному шляху координат і повертайте мінімальну вартість.

printМоже бути легко змінений , щоб вивести список координат для найкоротшого маршруту.


-36 байт з різними змінами.
notjagan

@notjagan Великі зміни, особливо використання orдля умовних умов. Дякую!
Розв’язання

1

APL (Dyalog Classic) , 33 байти

{⊃⌽,(⊢⌊⍵+(⍉3⌊/⊣/,⊢,⊢/)⍣2)⍣≡+\+⍀⍵}

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

{ } функція з аргументом

+\+⍀⍵ взяти часткові суми за рядками та стовпцями, щоб встановити песимістичну верхню межу на відстані шляху

( )⍣≡ повторити до зближення:

  • (⍉3⌊/⊣/,⊢,⊢/)⍣2хв відстаней до сусідів, тобто зробіть двічі ( ( )⍣2): додайте крайній лівий стовпець ( ⊣/,) до self ( ) і додайте стовпці праворуч ( ,⊢/), знайдіть мінімуми в горизонтальних трійках ( 3⌊/) та перемістіть ( )

  • ⍵+ додайте значення кожного вузла до його хвилин відстані до сусідів

  • ⊢⌊ спробуйте перемогти найкращі поточні дистанції

⊃⌽, нарешті, поверніть нижню праву клітинку

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.