火灾传播模拟器


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分钟后,这些细胞被燃烧,细胞繁殖到其相邻细胞。
  • 再过一分钟后,第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。
查理

谢谢。不,没关系。我的意思是一个数字,但可能有几个数字。该号码的范围1M*N
Luis Mendo

Answers:


12

MATLAB,235个 257 190 182 178字节

输入:Matrix Ap包含起始坐标的1x2向量。

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字节

感谢Kevin Cruijssen节省了1个字节

一个相当幼稚的实现。以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是虚假的。谢谢!
Arnauld

8

八度,67字节

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

在线尝试!

要显示中间结果,您可以尝试一下!

以地形为输入矩阵,a初始坐标为与地形相同大小的0-1 矩阵的函数。

实际上,实际上并不需要endfunction在示例中运行该示例。

说明:

重复在地形上应用形态学图像膨胀,然后从中减去烧伤的区域。

空洞的答案:

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=1n=0
rahnema17年

7

MATL26 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
这就是我所说的短代码!:-)
查理(Charlie)

4

Python 2,170个字节

s,m=input()
t={s}
r=0
while max(sum(m,[]))>0:
 r+=1
 for a,b in t|t:
	try:a<0<x;b<0<x;m[a][b]-=1;t|=m[a][b]==0and{(a+1,b),(a-1,b),(a,b+1),(a,b-1)}or t^t
	except:0
print r

在线尝试!


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}。然后该函数遍历每个点的元组,pm在该点处从矩阵中减去1 ,除非该值已为零。然后,它再次循环m查找所有值为零的点,并将该点的四个邻居添加到集合中p。这就是为什么我选择使用集合的原因,因为Python中的集合不允许重复的值(这会大大减少减法)。不幸的是,由于列表索引包装(例如list[-1] == list[len(list)-1]:),需要对索引进行约束,以使索引不会变为负数并向添加错误的坐标p

没什么特别的,仍然习惯打高尔夫球。这里肯定有改进的空间,我将继续努力。


您可以在“ 在线试用”上写一个执行示例,以便我们所有人都可以测试您的代码吗?
查理

@CarlosAlejo当然,将其添加到帖子中。
MooseOnTheRocks

4

APL(Dyalog)93 66 57字节

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

在线尝试!在线可视化!


此函数将terrain矩阵作为右参数,并将第一起火的坐标(从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),cell)的列表。取消高尔夫:

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)]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.