Симулятор поширення вогню


28

Припустимо, у нас є така матриця:

11111
12221
12321
12221
11111

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

11111        11111        11111        11011        10001        00000
12221  3 m.  12221  2 m.  12021  1 m.  11011  1 m.  00000  1 m.  00000
12321 -----> 12021 -----> 10001 -----> 00000 -----> 00000 -----> 00000
12221        12221        12021        11011        00000        00000
11111        11111        11111        11011        10001        00000

Пояснення:

  • Вогонь починається з [2,2] (на основі 0), час горіння якого становить 3.
  • Через 3 хвилини [1,2], [2,1], [2,3], [3,2] починають горіти.
  • Через 2 хвилини ці клітини закінчуються горінням і вогонь поширюється на всі сусідні клітини, але [0,2], [2,0], [2,4], [0,4] спалити потрібно лише ще 1 хвилину, тому
  • Через 1 хвилину ці клітини спалюються, і клітина поширюється до сусідніх клітин.
  • Ще через 1 хвилину решта осередків від кроку 3 закінчують горіння і вогонь поширюється на сусідні клітини (які вже згоріли, тому нічого не відбувається).
  • Через 1 останню хвилину вогонь закінчується спаленням всієї місцевості.

Тож рішення цієї справи - 8 хвилин. Якщо пожежа починається у верхній лівій осередку [0,0]:

11111     01111     00111     00011     00001     00000
12221  1  12221  1  02221  1  01221  1  00121  1  00011   1
12321 --> 12321 --> 12321 --> 02321 --> 01321 --> 00321  -->
12221     12221     12221     12221     02221     01221
11111     11111     11111     11111     11111     01111

00000     00000     00000     00000     00000
00000  1  00000  1  00000  1  00000  1  00000
00221 --> 00110 --> 00000 --> 00000 --> 00000
00221     00121     00020     00010     00000
00111     00011     00001     00000     00000

Тож тепер загальний час становить 10 хвилин.

Змагання

Враховуючи матрицю NxM (N> 0, M> 0) цілих значень, що представляють час, який кожну клітинку потрібно повністю споживати, напишіть найкоротшу програму / функцію, яка займає цю матрицю, і пару цілих чисел із положенням, у якому починається пожежа. , і повертає / друкує час, необхідний для пожежі, щоб повністю спожити всю територію.

  • Кожна клітина матиме позитивний (ненульовий) час горіння. Ви не можете припустити максимальне значення для комірок.
  • Матриця не повинна бути ні квадратною, ні симетричною.
  • Матриця може бути 0-індексованою або 1-індексованою, як вам подобається.
  • Позиція може бути задана у вигляді одного параметра з набором цілих чисел, двох окремих параметрів будь-якого іншого розумного формату.
  • Розміри матриці не можна вказати як вхідні параметри.
  • Вам не потрібно виводити кожен проміжний крок, лише кількість запитуваного часу. Але я не буду скаржитися, якщо кроки візуалізовані будь-яким чином.

Ще один приклад:

Fire starts at [1,1] (a '>' represents a minute):

4253   4253   4253   4153   4043   3033   2023    0001   0000
2213 > 2113 > 2013 > 1003 > 0002 > 0001 > 0000 >> 0000 > 0000 
1211   1211   1211   1111   1001   0000   0000    0000   0000

Output: 9

Це , тому може виграти найкоротша програма для кожної мови!


1
@LeanderMoesinger він повинен працювати з будь-якою матрицею. Я маю на увазі те, що ваша програма чи функція не можуть приймати розміри матриці як вхідні параметри, але, звичайно, ви можете обчислити ці розміри всередині вашого коду.
Чарлі

Чи можна вводити дані як одне число у порядку основного стовпця ? Тобто матричні записи нумеруються вниз, а потім поперек
Луїс Мендо

1
@ LuuMendo так, звичайно. Але зауважте, що час горіння кожної комірки може бути більше 9, якщо це має значення для частини "єдиного числа".
Чарлі

Спасибі. Ні, це не має значення. Я мав на увазі одне число, але можливо з кількома цифрами. Кількість буде варіюватися від 1доM*N
Луїс Mendo

Відповіді:


12

Матлаб, 235 257 190 182 178 байт

Вхід: Матриця A, 1х2 вектор, pщо містить початкові координати.

function t=F(A,p)
[n,m]=size(A);x=2:n*m;x(mod(x,n)==1)=0;B=diag(x,1)+diag(n+1:n*m,n);k=sub2ind([n m],p(1),p(2));t=max(distances(digraph(bsxfun(@times,((B+B')~=0),A(:))'),k))+A(k)

Пояснення:

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

5   7   7   10
5   2   2   10
4   5   2   6

ми отримуємо підключений графік:

графік

Де вузол 1 - верхній лівий матричний елемент, а вузол 12 - нижній правий елемент. За pданими початкових координат обчислюється найкоротший шлях до всіх інших вузлів. Тоді довжина найдовшого шляху цих найкоротших шляхів + час спалювання початкового вузла дорівнює часу спалення всієї матриці.

Невикольована та коментована версія із вихідними значеннями вибірки:

% some starting point
p = [3 2];
% some random 5x6 starting map, integers between 1:10
A = randi(10,5,6); 

function t=F(A,p)
% dimensions of A
[n,m] = size(A);
% create adjacency matrix
x=2:n*m;
x(mod(x,n)==1)=0;
B = diag(x,1)+diag(n+1:n*m,n);
B = B+B';
B = bsxfun(@times,(B~=0),A(:))';
% make graph object with it
G = digraph(B);
% starting node
k = sub2ind([n m], p(1), p(2));
% calculate the shortest distance to all nodes from starting point
d = distances(G,k);
% the largest smallest distance will burn down last. Add burntime of initial point
t = max(d)+A(k);

1
Ласкаво просимо до PPCG!
Стівен

Дуже приємний підхід і дуже добре пояснений!
Чарлі

Гей, оскільки це мій перший гольф, я повинен запитати, чи добре, що я пропускав ;після кожного рядка. У Matlab це запобігає відображенню результатів кожної команди в консолі. В даний час код ДУЖЕ балакучий і спамує консоль. Але оскільки це не суворий стан відмови, я зберігав це таким чином. Але це не має великого значення, це всього 4 байти
Leander Moesinger

1
@LeanderMoesinger чи спам переходить у ту саму область виводу, що і ваша програма? Якщо спам, наприклад, переходить у STDERR або еквівалент, а вихід переходить у STDOUT або еквівалент, вам слід буде добре їх видалити. Якби вони обидва виходять в одне місце, я б не знав.
Стівен

@ Це інша область виводу, але я її взагалі можу уникнути, поставивши все в один рядок. Thx для уточнення!
Leander Moesinger

9

JavaScript (ES6), 156 152 146 144 143 байт

Збережено 1 байт завдяки Kevin Cruijssen

Досить наївна реалізація. Приймає введення в синтаксис currying (a)(s), де a - 2D-масив, а s - масив з двох цілих чисел [ x, y ], що представляють 0-базисні координати вихідної позиції.

a=>s=>(g=t=>(a=a.map((r,y)=>r.map((c,x)=>(z=(h,v)=>(a[y+~~v]||[])[x+h]<1)(-1)|z(1)|z(0,-1)|z(0,1)|x+','+y==s&&c?u=c-1:c),u=-1),~u?g(t+1):t))(0)

Відформатовано та прокоментовано

a => s => (                                // given a and s
  g = t => (                               // g = recursive function with t = time counter
    a = a.map((r, y) =>                    // for each row r of the input array:
      r.map((c, x) =>                      //   for each cell c in this row:
        (                                  //     z = function that takes
          z = (h, v) =>                    //         2 signed offsets h and v and checks
            (a[y + ~~v] || [])[x + h] < 1  //         whether the corresponding cell is 0
        )(-1) | z(1) |                     //     test left/right neighbors
        z(0, -1) | z(0, 1) |               //     test top/bottom neighbors
        x + ',' + y == s                   //     test whether c is the starting cell
        && c ?                             //     if at least one test passes and c != 0:
          u = c - 1                        //       decrement the current cell / update u
        :                                  //     else:
          c                                //       let the current cell unchanged
      ),                                   //   end of r.map()
      u = -1                               //   start with u = -1
    ),                                     // end of a.map() --> assign result to a
    ~u ?                                   // if at least one cell was updated:
      g(t + 1)                             //   increment t and do a recursive call
    :                                      // else:
      t                                    //   stop recursion and return t
  )                                        // end of g() definition
)(0)                                       // initial call to g() with t = 0

Тестові справи


==0можна пограти в гольф, <1якщо я не помиляюся.
Кевін Кройсейсен

1
@KevinCruijssen Це справді безпечно, як undefined<1і фальш. Спасибі!
Арнольд

8

Октава, 67 байт

function n=F(s,a)n=0;do++n;until~(s-=a|=imdilate(~s,~(z=-1:1)|~z')

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

Для візуалізації проміжних результатів ви можете спробувати це!

Функція, яка приймає вхідну матрицю місцевості aта початкову координату як матрицю 0 і 1 такого ж розміру, як і місцевість.

Насправді, не потрібно, endfunctionоднак, запускати приклад в tio, його слід додати.

Пояснення:

Неодноразово застосовуйте морфологічну дилатацію зображення на місцевості та віднімайте від неї спалені ділянки.

Відповідь без волі:

function n = Fire(terrain,burned)
    n = 0;
    mask = [...
            0  1  0
            1  1  1
            0  1  0];
    while true
        n = n + 1;
        propagation = imdilate(~terrain, mask);
        burned = burned | propagation;
        terrain = terrain - burned;
        if all(terrain(:) == 0)
            break;
        end
    end
end

Це хороша відповідь, але, можливо, алгоритм рахує початковий стан як крок і повертає 11 замість 10 у вашому прикладі. Якщо я зміню початкову клітинку на [3 3], результат буде 9 замість 8.
Чарлі

@CarlosAlejo О, мені погано. Відповідь оновлено, змінено n=1на n=0.
rahnema1

7

MATL , 26 25 байт

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

`yy)qw(8My~1Y6Z+fhy0>z}@&

Формат введення:

  • Перший вхід - це матриця, що використовується ;як роздільник рядків.

  • Другий вхід - це єдине число, яке адресує запис матриці в порядку, заснованому на 1 стовпчику (дозволеному викликом). Наприклад, основна колонка координати кожного запису в матриці 3 × 4 задається через

    1  4  7 10
    2  5  8 11
    3  6  9 12
    

    Так, наприклад, відповідають 1-координатні координати (2,2) 5.

Спробуйте в Інтернеті! Або перевірити всі тестові випадки .

Пояснення

Код підтримує список записів, які горять. При кожній ітерації всі записи цього списку зменшуються. Коли запис досягає нуля, його сусідні записи додаються до списку. Для збереження байтів записи, які досягають нуля, не видаляються зі списку; натомість вони продовжують «горіти» негативними значеннями. Цикл виходить, коли жодна із записів не має позитивних значень.

Дивіться програму, що працює за кроком, з трохи зміненим кодом.

Коментований код:

`      % Do...while
  yy   %   Duplicate top two arrays (matrix and array of positions to be decremented)
       %   In the first iteration this implicitly takes the two inputs
  )    %   Reference indexing. This gives the values that need to be decremented
  q    %   Decrement
  w    %   Swap. This brings the array of positions that have been decremented to top
  (    %   Assignment indexing. This writes the decremented values back into their
       %   positions
  8M   %   Push array of positions again
  y    %   Duplicate decremented matrix
  ~    %   Negate. This replaces zeros by 1, and nonzeros by 0
  1Y6  %   Push predefined literal [0 1 0; 1 0 1; 0 1 0] (4-neighbourhood)
  Z+   %   2D convolution, maintaining size
  f    %   Find: gives column-major indices of neighbours of totally burnt entries
  h    %   Concatenate. This updates the array of positions to be decremented
  y    %   Duplicate decremented matrix
  0>   %   This gives 1 for positive entries, and 0 for the rest
  z    %   Number of nonzeros. This is the loop condition (*)
}      % Finally (execute before exiting loop)
  @    %   Push iteration number. This is the output
  &    %   Specify that the final implicit display function will display only the top
       %   of the stack
       % Implicit end. If the top of the stack (*) is not 0 (i.e. there are entries
       % that have not been totally burnt) the loop proceeds with the next iteration.
       % Else the "finally" branch is executed and the loop is exited
       % Implicit display (only top of the stack)

2
Тепер це я називаю короткий код! :-)
Чарлі


4

Пітон 3 , 277 266 байт

def f(m,s):
 p={s};w=len(m);t=0
 while sum(sum(m,[])):
  t+=1;i=0
  for x,y in p:
   try:m[x][y]=max(0,m[x][y]-1)
   except:0
  for v in sum(m,[]):
   if v<1:
    for l in[(1,0),(-1,0),(0,1),(0,-1)]:a,b=max(0,i%w+l[0]),max(0,i//w+l[1]);p.add((a,b))
   i+=1
 print(t)

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

Визначає функцію, fяка займає 2D матрицю та кортеж точок. Перше , що робить функція , це визначити набір кортежів , що містять початкове значення кортежу передається в: p={s}. Потім функція проходить через кожен кортеж точок pі віднімає одну з матриці mв цій точці, якщо тільки значення вже не дорівнює нулю. Потім він mзнову проходить цикл пошуку всіх точок зі значенням нуля та додавання чотирьох сусідів цієї точки до набору p. Ось чому я вирішив використовувати набір, тому що множини в Python не дозволяють повторювати значення (що б сильно відкрутити віднімання). На жаль, через обгортання індексу списку (наприклад list[-1] == list[len(list)-1]:) індекси потрібно обмежувати, щоб вони не ставали негативними та додавали неправильні координати p.

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


Чи можете ви написати приклад виконання на веб-сайті " Спробуйте в Інтернеті", щоб ми всі могли перевірити ваш код?
Чарлі

@CarlosAlejo Звичайно, додав це до публікації.
MooseOnTheRocks

4

APL (Діалог) , 93 66 57 байт

{⍵{^/,0≥⍺:0⋄1+x∇⍵∨{∨/,⍵∧⍲/¨2|⍳3 3}⌺3 30=x←⍺-⍵}(⊂⍺)≡¨⍳⍴⍵}

Спробуйте в Інтернеті! або Візуалізуйте його в Інтернеті!


Ця функція приймає матрицю місцевості як правий аргумент, а координати (на основі 1) першого вогню як лівий аргумент. Повертає кількість хвилин, необхідних для спалення всього.


Оновлення

Нарешті знайшов спосіб пограти в гольф за допомогою функції розкидання.
* Зітхнути * було б набагато простіше, якби світ був тороїдним .


TIO щойно оновлений до Dyalog 16.0 , що означає, що зараз у нас є новий блискучий оператор трафарету


Дуже приємна відповідь! Проміжний вихід допомагає візуалізувати прогрес!
Чарлі

2

Python 2 , 268 байт

def f(m,y,x):t,m[y][x]=m[y][x],0;g(m,t)
def g(m,t):
	n,h,w=map(lambda r:r[:],m),len(m),len(m[0])
	for l in range(h*w):r,c=l/h,l%h;n[r][c]-=m[r][c]and not all(m[r][max(c-1,0):min(c+2,w+1)]+[m[max(r-1,0)][c],m[min(r+1,h-1)][c]])
	if sum(sum(m,[])):g(n,t+1)
	else:print t

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

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

* Примітка: мій "Спробуйте онлайн!" код включає в себе бонус вхідної помилки у нижньому колонтитулі. Мені подобається спостерігати за ходом алгоритму.


2

Haskell , 138 133 байт

u#g|all((<=0).snd)g=0|2>1=1+(u:[[(x+1,y),(x-1,y),(x,y-1),(x,y+1)]|((x,y),0)<-n]>>=id)#n where n=d<$>g;d p|elem(fst p)u=pred<$>p|2>1=p

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

Припускає, що введення - це список ((x, y), комірки). Безголівки:

type Pos = (Int, Int)

ungolfed :: [Pos] -> [(Pos, Int)] -> Int
ungolfed burning grid
  | all ((<=0).snd) grid = 0 
  | otherwise = 1 + ungolfed (burning ++ newburning) newgrid
 where
  newgrid = map burn grid
  burn (pos,cell) | pos `elem` burning = (pos, cell - 1)
                  | otherwise = (pos, cell)
  newburning = do
    ((x,y),cell) <- newgrid
    guard (cell <= 0)
    [(x+1,y),(x-1,y),(x,y-1),(x,y+1)]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.