火災伝播シミュレータ


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インデックスを付けることができます。
  • 位置は、整数のタプルを持つ単一のパラメーター、その他の妥当な形式の2つの別個のパラメーターとして指定できます。
  • マトリックスの次元を入力パラメーターとして指定することはできません。
  • すべての中間ステップを出力する必要はなく、求められた時間だけを出力する必要があります。しかし、手順が何らかの形で視覚化されていても文句は言いません。

もう一つの例:

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
ルイスMendo

回答:


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では、これらは各コマンドの結果がコンソールに表示されるのを防ぎます。現在、コードは非常におしゃべりで、コンソールをスパムします。しかし、それは厳密な障害状態ではないため、そのように保ちました。しかし、それは
大した

1
@LeanderMoesingerは、プログラムの出力と同じ出力領域にスパムを送信しますか?たとえば、スパムがSTDERRまたは同等のものになり、出力がSTDOUTまたは同等のものになった場合、それらを削除しても問題ありません。両方が同じスポットに出力される場合、私は知りません。
スティーブン

@これは異なる出力領域ですが、すべてを1行にまとめることで完全に回避できます。明確化のためのThx!
リアンダーモージンガー

9

JavaScript(ES6)、156 152 146 144 143バイト

Kevin Cruijssenのおかげで1バイト節約

かなり素朴な実装。構文カリー化で入力を受け取り(a)(s)、2次元アレイであり、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私が間違っていなければゴルフをすることができます。
ケビンCruijssen

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

これはいい答えですが、おそらくアルゴリズムは初期状態をステップとしてカウントし、例では10ではなく11を返します。初期セルを[3 3]に変更すると、結果は8ではなく9になります
チャーリー

@CarlosAlejoオハイオ州、私の悪い。更新さn=1れた回答がに変更されましたn=0
rahnema1

7

MATL26 25バイト

この辺りで最もゴルフ好きな言語を使って、もっともっと答えを見つけたかった

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

入力形式は次のとおりです。

  • 最初の入力は、;行区切りとして使用する行列です。

  • 2番目の入力は、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の3277の 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}。関数は、値が既にゼロでない限り、ポイントのすべてのタプルを通過し、そのポイントでpマトリックスから1を減算しmます。次にm、値0のすべてのポイントを見つけて、そのポイントの4つの近傍をセットに追加し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←⍺-⍵}(⊂⍺)≡¨⍳⍴⍵}

オンラインでお試しください!または、オンラインで視覚化してください!


この関数は、右の引数として地形行列を取り、左の引数として最初の発砲の座標(1ベース)を取ります。すべてを書き込むのに必要な分数を返します。


更新

最後に、スプレッド関数を下にゴルフする方法を見つけました。
*ため息*それは世界がトロイダルだった場合、それはとても簡単だろう。


TIO がDyalog 16.0アップグレードされました。つまり、光沢のある新しいステンシルオペレーターができました。


とてもいい答えです!中間出力は、進行状況を視覚化するのに役立ちます!
チャーリー

2

Python 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

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