화재 전파 시뮬레이터


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이 아닌 양의 레코딩 시간이 있습니다. 셀의 최대 값을 가정 할 수 없습니다.
  • 행렬은 정사각형이나 대칭 일 필요는 없습니다.
  • 매트릭스는 원하는대로 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 그것은 모든 매트릭스와 함께 작동해야합니다. 내 말은 프로그램이나 함수가 행렬의 차원을 입력 매개 변수로 받아 들일 수는 없지만 물론 코드 내에서 해당 차원을 계산할 수 있다는 것입니다.
Charlie

입력을 열 주요 순서 로 단일 숫자로 취할 수 있습니까 ? 즉, 행렬 항목은 번호가 매겨진 다음에 걸쳐
Luis Mendo

1
물론 @LuisMendo입니다. 그러나 "단일 숫자"부분에 중요한 경우 모든 셀의 레코딩 시간이 9보다 클 수 있습니다.
Charlie

감사. 아니, 상관 없어 나는 하나의 숫자를 의미했지만 여러 숫자가있을 수 있습니다. 수는 범위 것 1까지M*N
루이스 Mendo

답변:


12

MATLAB, 235 257 190 182 178 바이트

입력 : Matrix A, p시작 좌표를 포함하는 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다른 모든 노드까지의 최단 경로가 계산됩니다. 그런 다음 최단 경로의 최장 경로 길이 + 초기 노드를 태우는 시간은 전체 매트릭스를 태우는 시간과 같습니다.

샘플 시작 값이 포함 된 ungolfed 및 주석이 달린 버전 :

% 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에 오신 것을 환영합니다!
Stephen

아주 좋은 접근 방식과 잘 설명되어 있습니다!
Charlie

이봐, 이것이 내 첫 골프이기 때문에, 나는 ;각 줄 이후를 생략해도 괜찮은지 물어봐야합니다 . Matlab에서는 각 명령의 결과가 콘솔에 표시되는 것을 방지합니다. 현재 코드는 매우 번거롭고 콘솔을 스팸으로 만듭니다. 그러나 그것이 엄격한 실패 상태가 아니기 때문에 나는 그것을 그렇게 유지했습니다. 그러나 그것은 중요하지 않습니다. 단지 4 바이트입니다
Leander Moesinger

1
@LeanderMoesinger 스팸은 프로그램 출력과 동일한 출력 영역으로 이동합니까? 예를 들어, 스팸이 STDERR 또는 이와 동등한 것으로 출력되고 출력이 STDOUT 또는 동등한 것으로 들어가는 경우이를 제거해야합니다. 둘 다 동일한 지점에서 출력되면 모르겠습니다.
Stephen

@ 그것은 다른 출력 영역이지만 모든 것을 한 줄에 넣으면 완전히 피할 수 있습니다. 설명을위한 Thx!
Leander Moesinger 2012 년

9

자바 스크립트 (ES6) 156 152 146 144 143 바이트

Kevin Cruijssen 덕분에 1 바이트 절약

다소 순진한 구현입니다. 카레 구문에서 입력을받습니다 (a)(s). 여기서 a 는 2D 배열이고 s 는 시작 위치의 0 기반 좌표를 나타내는 두 개의 정수 [ x, y ] 의 배열입니다 .

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내가 실수하지 않으면 골프를 칠 수 있습니다 .
Kevin Cruijssen

1
@KevinCruijssen 이것은 허위이므로 안전 undefined<1합니다. 감사!
Arnauld 2016 년

8

옥타브, 67 바이트

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

온라인으로 사용해보십시오!

중간 결과를 시각화하려면 다음을 시도하십시오!

지형의 입력 행렬과 지형 a과 크기가 같은 0의 행렬로 초기 좌표 를 취하는 함수입니다 .

실제로 endfunctiontio에서 예제를 실행할 필요는 없습니다 .

설명:

지형에 형태 학적 이미지 확장을 반복해서 적용하고 구운 영역을 빼냅니다.

답이없는 답변 :

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

이것은 좋은 대답이지만 알고리즘은 초기 상태를 단계로 계산하고 예제에서 10 대신 11을 반환합니다. 초기 셀을 [3 3]으로 변경하면 결과는 8 대신 9가됩니다.
Charlie

@CarlosAlejo OH, 내 나쁜. 답변이로 변경 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.

온라인으로 사용해보십시오! 또는 모든 테스트 사례를 확인하십시오 .

설명

코드는 레코딩중인 항목 목록을 유지 관리합니다. 각 반복에서 해당 목록의 모든 항목이 감소합니다. 항목이 0에 도달하면 해당 항목이 목록에 추가됩니다. 바이트를 저장하기 위해 0에 도달 한 항목은 목록에서 제거되지 않습니다. 대신 음수 값으로 "굽기"를 유지합니다. 양수 값을 가진 항목이 없으면 루프가 종료됩니다.

약간 수정 된 코드 로 단계별실행중인 프로그램을 참조하십시오 .

주석이 달린 코드 :

`      % 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

파이썬 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)

온라인으로 사용해보십시오!

f2D 행렬과 튜플 포인트를 취하는 함수 를 정의합니다 . 함수가하는 첫 번째 일은 다음에 전달 된 초기 튜플 값을 포함하는 튜플 세트를 정의하는 것입니다 p={s}. 그런 다음 함수 는 값이 이미 0이 아닌 한 모든 포인트 튜플을 통과 하고 해당 포인트 p의 매트릭스 m에서 1을 뺍니다 . 그런 다음 m값이 0 인 모든 점을 찾아 해당 점의 네 이웃을 세트에 추가합니다 p. 이것이 파이썬에서 세트가 중복 값을 허용하지 않기 때문에 세트를 사용하기로 선택한 이유입니다 (빼기를 많이 망칠 것입니다). 불행히도,리스트 인덱스 래핑 (예 :)으로 인해 list[-1] == list[len(list)-1]인덱스가 음수로 이동하지 않고 잘못된 좌표를 추가하도록 제한해야합니다 p.

골프에 익숙하지 않은 특별한 것은 없습니다. 확실히 개선의 여지가 있습니다.


Try it online 에서 실행 예제를 작성 하여 코드를 테스트 할 수 있습니까?
Charlie

@CarlosAlejo 물론 포스트에 추가했습니다.
MooseOnTheRocks 2018 년

4

APL (Dyalog) , 93 66 57 바이트

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

온라인으로 사용해보십시오! 또는 온라인으로 시각화하십시오!


이 함수는 터 레인 매트릭스를 오른쪽 인수로 사용하고 첫 번째 발사의 좌표 (1 기반)를 왼쪽 인수로 사용합니다. 모든 레코딩에 필요한 시간 (분)을 반환합니다.


업데이트

마지막으로 스프레드 기능을 사용하지 않는 방법을 찾았습니다.
* 한숨 * 세상이 환상적 이라면 훨씬 쉬울 것 입니다.


TIO 가 Dyalog 16.0으로 업그레이드되었습니다. 이제 새로운 스텐실 운영자가 생겼습니다.


아주 좋은 답변입니다! 중간 출력은 진행 상황을 시각화하는 데 도움이됩니다!
Charlie

2

파이썬 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

하스켈 , 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)]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.