Симулятор распространения огня

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, заканчивают гореть, и огонь распространяется на соседние ячейки (которые уже сожжены, поэтому ничего не происходит).
  • Через одну последнюю минуту огонь прекращает сжигать всю местность.

Таким образом, решение по этому делу составляет 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
@ LuisMendo да, конечно. Но обратите внимание, что время горения каждой ячейки может быть больше 9, если это имеет значение для части «одно число».
Чарли
Спасибо. Нет, это не важно. Я имел в виду одно число, но, возможно, с несколькими цифрами. Число будет варьироваться от 1доM*N
Луис Мендо

Ответы:

12

Matlab, 235 257 190 182 178 байт

Вход: матрица A, вектор 1x2, 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 байта
Леандер Мезингер
1
@LeanderMoesinger попадает ли спам в ту же область вывода, что и ваша программа? Если спам, например, переходит в STDERR или эквивалентный, а вывод переходит в STDOUT или эквивалентный, вы можете удалить их. Если бы они оба выводили в одном месте, я бы не знал.
Стивен
@ Это другая область вывода, но я могу полностью избежать этого, поместив все в одну строку. Спасибо за разъяснение!
Леандер Мезингер
9

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

Сохранено 1 байт благодаря Кевину Круйссену

Довольно наивная реализация. Принимает ввод в синтаксисе карри (a)(s), где a - это двумерный массив, а 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

Контрольные примеры

Arnauld
источник
==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
rahnema1
источник
Это хороший ответ, но, возможно, алгоритм считает начальное состояние как шаг и возвращает 11 вместо 10 в вашем примере. Если я изменю исходную ячейку на [3 3], то
Чарли
@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

Python 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.

Ничего особенного, все еще привыкаешь к игре в гольф. Определенно, здесь есть место для улучшения, и я буду продолжать это делать.

MooseOnTheRocks
источник
Не могли бы вы написать пример выполнения в разделе «Попробуйте онлайн», чтобы мы все могли проверить ваш код?
Чарли
@CarlosAlejo Конечно, добавил его в пост.
MooseOnTheRocks
4

APL (Dyalog) , 93 66 57 байтов

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

Попробуйте онлайн! или визуализируй это онлайн!


Эта функция принимает матрицу ландшафта в качестве правого аргумента и координаты (на основе 1) первого огня в качестве левого аргумента. Возвращает количество минут, необходимое для записи всего.


Обновления

Наконец-то нашел способ поиграть в гольф по спреду
* Вздох * было бы намного проще, если бы мир был тороидальным .


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

TwiNight
источник
Очень хороший ответ! Промежуточный вывод помогает визуализировать прогресс!
Чарли
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), ячейка). Ungolfed:

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)]
bartavelle
источник