迷路のカットポイント


13

迷路は、任意の便利な形式で0(壁)と1(歩行可能な空間)のマトリックスとして与えられます。各セルは、4つ(またはそれ以下)の直交隣接ノードに接続されていると見なされます。連結成分は全て過渡互いに接続歩行セルの集合です。あなたの仕事は、カットポイントを特定することです-歩きやすいセルは、壁になった場合、接続されたコンポーネントの数を変更します。それらの位置でのみ1-sのブール行列を出力します。目標は、コードの最小バイトでそれを行うことです。

入力行列は、少なくとも3行3列で構成されます。そのセルの少なくとも1つは壁で、少なくとも1つは歩行可能です。関数またはプログラムは、TIO(または言語がTIOでサポートされていない場合は自分のコンピューター)で1分以内に以下の例を処理できる必要があります。

in:
11101001
11011101
00000001
11101111
11110101
00011111
10110001
11111111
out:
01000000
00001001
00000001
00000101
00110000
00010000
00000000
11100000

in:
1111111111111111
1000000000000001
1111111111111101
0000000000000101
1111111111110101
1000000000010101
1011111111010101
1010000001010101
1010111101010101
1010101111010101
1010100000010101
1010111111110101
1010000000000101
1011111111111101
1000000000000001
1111111111111111
out:
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000

in:
1011010001111010
1111111011101101
1110010101001011
1111001110010010
1111010000101001
0111101001000101
0011100111110010
1001110011111110
0101000011100011
1110110101001110
0010100111000110
1000110111011010
0100101000100101
0001010101100011
1001010000111101
1000111011000010
out:
0000000000111010
1011110001001000
0000000000000011
0000000100010000
0000010000101000
0000001000000100
0000000011000000
1001100000011110
0000000001000010
0110100001000110
0000100101000010
1000100000000000
0100001000000100
0000000100100001
0000010000111000
0000010000000010

そのため、すべてのサブグラフですべてのブリッジを見つける
-HyperNeutrino

1
課題は、小さなマトリックスの段階的な例から恩恵を受けると思います。
ミスターXcoder

1
@HyperNeutrino ブリッジは何か異なるものです- 削除された接続コンポーネントの数が増えるエッジ(頂点ではありません)
-ngn

1
@HyperNeutrinoも、サブグラフ接続されたコンポーネント
-ngn

1
@Notatreeそのとおりです。私が間違えました。今すぐ修正するには遅すぎますが、楽しさを損なわないことを願っています。
ngn

回答:


3

スタックス、40 バイト

Çóê↓â.Φ}╞│*w<(♦◙¼ñ£º█¢,D`ì♥W4·☺╛gÇÜ♠╗4D┬

テストケースの実行とデバッグ

このプログラムは、行を含むスペースで区切られた文字列として入力を受け取ります。出力は同じ形式です。解凍されたアスキー表現は次のとおりです。

{2%{_xi48&GxG=-}_?m}{'1'2|e{"12|21".22RjMJguHgu%

島を数えるための基本的な操作はこのように機能します。

  1. 最初'1'をで置き換え'2'ます。
  2. 正規表現はに置き換え'12|21'られ'22'ます。
  3. スペースで分割します。
  4. 転置行列。
  5. 文字列が繰り返されるまで、2から繰り返します。
  6. '1'文字列にaがなくなるまで1から繰り返します。繰り返しの数は、島の数です。

{               start map block over input string, composed of [ 01]
  2%            mod by 2. space and 0 yield 0. 1 yields 1. (a)
  {             start conditional block for the 1s.
    _           original char from string (b)
    xi48&       make copy of input with current character replaced with 0
    G           jump to unbalanced }, then return; counts islands (c)
    xG          counts islands in original input (d)
    =           are (c) and (d) equal? 0 or 1 (e)
    -           b - e; this is 1 iff this character is a bridge
  }             end conditional block
  _?            execute block if (a) is 1, otherwise use original char from string
m               close block and perform map over input
}               goto target - count islands and return
{               start generator block
  '1'2|e        replace the first 1 with a 2
  {             start generator block
    "12|21".22R replace "12" and "21" with "22"
    jMJ         split into rows, transpose, and rejoin with spaces
  gu            generate values until any duplicate is encountered
  H             keep the last value
gu              generate values until any duplicate is encountered
%               count number of iterations it took

ボーナス44バイトプログラム -このバージョンは、グリッド形式を使用して入出力します。


2番目の例を1分以内にコンピューターで処理しますか?
ngn

@ngn:Chromeのこのミッドレンジラップトップで、41の3つの例すべてを実行します。また、メインリンクを修正しました。誤って古い非動作バージョンに設定したままにしました。
再帰的

3

MATL、26バイト

n:"GG0@(,w4&1ZIuz]=~]vGZye

入力は、;行セパレータとして使用する数値行列です。

オンラインでお試しください!または、すべてのテストケースを確認します

説明

n           % Implicit input: matrix. Push number of elements, N
:           % Range: gives [1 2 ... N]
"           % For each k in [1 2 ... N]
  GG        %   Push input matrix twice
  0@(       %   Write 0 at position k (in column-major order: down, then across).
            %   The stack now contains the original matrix and a modified matrix
            %   with 0 at position k
  ,         %   Do twice
    w       %     Swap
    4       %     Push 4. This specifies 4-element neighbourhood
    &1ZI    %     Label each connected component, using the specified
            %     neighbourhood. This replaces each 1 in the matrix by a
            %     positive integer according to the connected component it
            %     belongs to
    u       %     Unique: gives a vector of deduplicate elements
    z       %     Number of nonzeros. This is the number of connected components
  ]         %   End
  =~        %   Are they different? Gives true of false
]           % End
v           % Concatenate stack into a column vector
GZye        % Reshape (in column-major order) according to size of input matrix.
            % Implicit display

2

パール5-p0 105の 101 96 93 90 89バイト

入力のb代わりに使用1します。

STDINのマトリックスが改行で終了していることを確認してください

#!/usr/bin/perl -p0
s%b%$_="$`z$'";s:|.:/
/>s#(\pL)(.{@{-}}|)(?!\1)(\pL)#$&|a.$2.a#se&&y/{c/z />0:seg&/\B/%eg

オンラインでお試しください!

3レベルの置換を使用します!

この87バイトバージョンは、入力と出力の両方の形式で解釈が容易ですが、出力で3つの異なる文字を使用するため競合していません。

#!/usr/bin/perl -0p
s%b%$_="$`z$'";s:|.:/
/>s#(\w)(.{@{-}}|)(?!\1)(\w)#$&|a.$2.a#se&&y/{c/z />0:seg&/\B/%eg

オンラインでお試しください!

s両方のバージョンで別のバイト(正規表現修飾子)を(改行ではなく)異なる(英数字以外の)文字を行ターミネーターとして使用することで簡単に保存できますが、それにより入力がまったく読めなくなります。

使い方

代替を検討する

s#(\w)(.{columns}|)(?!1)(\w)#c$2c#s

これにより、水平方向または垂直方向に異なる隣同士の2つの文字が検索され、に置き換えられcます。パスが完全に文字で構成されている迷路ではb、文字は同じであるため何も起こりませんが、文字の1つが別の文字(たとえばz)に置き換えられるとすぐに、その文字と隣人が置き換えられc、繰り返し適用されますcfrom seedで接続されたコンポーネントを塗りつぶしzます。

ただし、この場合、完全な塗りつぶしは必要ありません。隣の腕の1つだけを埋めたいzので、最初のステップの後、私はz行って欲しいです。それはすでにc$2c交換で機能しますが、後で同じポイントから別のアームに沿ってフラッドフィルを再開したいのですcが、元々どのsが元々あったのかわかりzません。だから代わりに私は

s#(\w)(.{columns}|)(?!\1)(\w)#$&|a.$2.a#se

b | ais cb | cis cz | ais {です。だから、パスを持つ迷路にで構成bし、種子zの最初のステップでは、b置き換えられてしまいますcz置き換えられてしまいます{手紙ではなく、一致しない\wので、さらにフィルが発生することはありません。cしかし行くさらにフラッドフィルを続けると、シードの一つの隣接アームが満たされます。たとえば、

  b                      c
  b                      c
bbzbb       becomes    bb{bb
  b                      b
  b                      b

その後、すべてのcを何らかの非文字(例-:)で置き換え{z再度置き換えてフラッドフィルを再開できます。

  -                      -
  -                      -
bbzbb       becomes    cc{bb
  b                      b
  b                      b

シードのすべてのネイバーが変換されるまでこのプロセスを繰り返します。その後、もう一度置き換え{z塗りつぶします:

  -                      -
  -                      -
--z--       stays      --z--
  -                      -
  -                      -

zとの変換を行うにはネイバーが存在しないため、最後に残ります。これにより、次のコードフラグメントで何が起こるかが明確になります。

/\n/ >                                    

最初の改行を見つけます。開始オフセットは現在@-

s#(\w)(.{@{-}}|)(?!\1)(\w)#$&|a.$2.a#se

上記で説明した正規表現は@{-}、列の数として(単純な@-perlパーサーを混同し、適切に置換しないため)

&&

/\n/常に成功し、置換がいる限り、我々はフラッドフィルまだできる限り真実です。その&&ため、片方のアームのフラッドフィルが完了すると、その後の部分が実行されます。そうでない場合、左側は空の文字列に評価されます

y/{c/z / > 0

フラッドフィルを再起動し、以前のフラッドフィルが何かをした場合は1を返します。それ以外の場合は、空の文字列を返します。このコード全体が内部にラップされています

s:|.: code :seg

したがって、これがシード位置にある開始文字列$_で実行されると、z内部のコードの一部が何回も実行され、ほとんどの場合1、近隣のアームがあふれるたびに何も返されません。に接続されているコンポーネントが接続さ$_れている1のと同じ数だけ、効果的に破棄および置換されzます。ループはコンポーネントサイズの合計+アーム回数まで実行する必要があることに注意してください。ただし、「改行を含む文字数* 2 + 1」回になるので問題ありません。

が存在しない場合1(空の文字列、孤立した頂点)、または1つ以上の腕がある場合(2つ以上)、迷路は切断されます1。これは、正規表現を使用して確認できます/\B/(これは、古いperlバージョンの0代わりに表示さ1れます。どちらが間違っているかは議論の余地があります)。残念ながら、一致しない場合、これはの代わりに空の文字列を返します0。ただし、s:|.: code :segは、常に奇数を返すように設計されているため&/\B/これでを実行すると、0またはが返され1ます。

残っているのは、入力配列全体を歩くことと、各歩行可能位置でz接続されたアームを使用してシードを数えることだけです。それは簡単にできます:

s%b%$_="$`z$'"; code %eg

唯一の問題は、歩行不可能な位置では古い値が保持されることです。0そこにsが必要なので、元の入力配列は元の置換で0歩行不可能な位置と0一致\wにある必要があり、フラッドフィルをトリガーします。そのため、\pL代わりに使用します(文字にのみ一致)。


2

Java 8、503 489 459 455バイト

int R,C,v[][];m->{int c[][]=new int[R=m.length][C=m[0].length],r[][]=new int[R][C],i=R*C,t,u;for(;i-->0;)c[t=i/C][u=i%C]=m[t][u];for(;++i<R*C;r[t][u]=i(c)!=i(m)?1:0,c[t][u]=m[t][u])c[t=i/C][u=i%C]=0;return r;}int i(int[][]m){int r=0,i=0,t,u;for(v=new int[R][C];i<R*C;)if(m[t=i/C][u=i++%C]>v[t][u]){d(m,t,u);r++;}return r;}void d(int[][]m,int r,int c){v[r][c]=1;for(int k=-3,t,u;k<4;k+=2)if((t=r+k/2)>=0&t<R&(u=c+k%2-k/2)>=0&u<C&&m[t][u]>v[t][u])d(m,t,u);}

@ceilingcatのおかげで-18バイト。

説明:

オンラインでお試しください。

int R,C,                    // Amount of rows/columns on class-level
    v[][];                  // Visited-matrix on class-level

m->{                        // Method with int-matrix as both parameter and return-type
  int c[][]=new int[R=m.length][C=m[0].length],
                            //  Create a copy-matrix, and set `R` and `C`
      r[][]=new int[R][C],  //  Create the result-matrix
      i=R*C,                //  Index-integer
      t,u;                  //  Temp integers
  for(;i-->0;)              //  Loop `i` over each cell:
    c[t=i/C][u=i%C]=m[t][u];//   And copy the values of the input to the copy-matrix
  for(;++i<R*C              //  Loop over the cells again:
      ;                     //    After every iteration:
       r[t][u]=i(c)!=i(m)?  //     If the amount of islands in `c` and `m` are different
        1                   //      Set the current cell in the result-matrix to 1
       :                    //     Else:
        0,                  //      Set it to 0
       c[t][u]=m[t][u])     //     And set the copy-value back again
    c[t=i/C][u=i%C]=0;      //   Change the current value in the copy-matrix to 0
  return r;}                //  Return the result-matrix

// Separated method to determine the amount of islands in a matrix
int i(int[][]m){
  int r=0,                  //  Result-count, starting at 0
      i=0,                  //  Index integer
      t,u;                  //  Temp integers
  for(v=new int[R][C];      //  Reset the visited array
      i<R*C;)               //  Loop over the cells
    if(m[t=i/C][t=i++%C]    //   If the current cell is a 1,
       >v[t][u]){           //   and we haven't visited it yet:
      d(m,i,j);             //    Check every direction around this cell
      r++;}                 //    And raise the result-counter by 1
   return r;}               //  Return the result-counter

// Separated method to check each direction around a cell
void d(int[][]m,int r,int c){
  v[r][c]=1;                //  Flag this cell as visited
  for(int k=-3,u,t;k<4;k+=2)//  Loop over the four directions:
    if((t=r+k/2)>=0&t<R&(u=c+k%2-k/2)>=0&u<C
                            //   If the cell in the direction is within bounds,
       &&m[t][u]            //   and it's a path we can walk,
         >v[t][u])          //   and we haven't visited it yet:
      d(m,i,j);}            //    Do a recursive call for this cell

1

Python 2、290バイト

lambda m:[[b([[C and(I,J)!=(i,j)for J,C in e(R)]for I,R in e(m)])!=b(eval(`m`))for j,c in e(r)]for i,r in e(m)]
def F(m,i,j):
	if len(m)>i>=0<=j<len(m[i])>0<m[i][j]:m[i][j]=0;F(m,i,j+1);F(m,i,j-1);F(m,i+1,j);F(m,i-1,j)
b=lambda m:sum(F(m,i,j)or c for i,r in e(m)for j,c in e(r))
e=enumerate

オンラインでお試しください!

ロッドのおかげで
-11バイトリンのおかげで-11バイト


1
これは、使用する短いですF(m,i,j)、要素ごとに11のバイトを保存する
ロッド

for q in((i,j+1),(i,j-1),(i+1,j),(i-1,j)):-> for q in(i,j+1),(i,j-1),(i+1,j),(i-1,j):-rm外側括弧
-ngn

F暗黙的に戻るためNone、のF(m,i,j)or c代わりに使用できます[F(m,i,j)]and c
リン

また、and m[i][j]は、、>0<m[i][j]および[q[:]for q in m]が可能eval(`m`)です。
リン

@Lynnはeval( 'm')のことですか?それは同じリストインスタンスを返しませんか?
-ngn


1

Javascript 122バイト

複数行の文字列としての入力/出力。

m=>m.replace(/./g,(v,p,m,n=[...m],f=p=>n[p]==1&&(n[p]=0,v=f(p-1)+f(p+1)+f(p-w)+f(p+w)-1?1:0,1))=>(f(p),v),w=~m.search`\n`)

歩行可能なセルごとに、ブロックを配置し、4つの隣接セルを埋めようとします。現在のセルがカットポイントでない場合、開いている隣接セルから開始すると、すべてのセルが塗りつぶされます。そうでない場合、すべての隣接セルに到達するために複数のフィル操作が必要になります。

少ないゴルフ

m=>{
  w = m.search('\n') + 1; // offset to the next row
  result = [...m].map( // for each cell
     ( v, // current value
       p  // current position
     ) => {
     n = [...m]; // work on a copy of the input
     // recursive fill function from position p
     // returns 1 if managed to fill at least 1 cell
     fill = (p) => {
        if (n[p] == 1)
        {
           n[p] = 0;
           // flag will be > 1 if the fill from the current point found disjointed areas
           // flag will be 0 if no area could be filled (isolated cell)
           var flag = fill(p+1) + fill(p-1) + fill(p+w) + fill(p-w);
           // v is modified repeatedly, during recursion
           // but I need the value at top level, when fill returns to original caller
           v = flag != 1 ? 1 : 0;
           return 1; // at least 1 cell filled
        }
        else
           return 0; // no fill
     }
     fill(p)
     return v // orginal value or modified by fill function
  }) 
}

テスト

var F=
m=>m.replace(/./g,(v,p,m,n=[...m],f=p=>n[p]==1&&(n[p]=0,v=f(p-1)+f(p+1)+f(p-w)+f(p+w)-1?1:0,1))=>(f(p),v),w=~m.search`\n`)

var test=`in:
11101001
11011101
00000001
11101111
11110101
00011111
10110001
11111111
out:
01000000
00001001
00000001
00000101
00110000
00010000
00000000
11100000

in:
1111111111111111
1000000000000001
1111111111111101
0000000000000101
1111111111110101
1000000000010101
1011111111010101
1010000001010101
1010111101010101
1010101111010101
1010100000010101
1010111111110101
1010000000000101
1011111111111101
1000000000000001
1111111111111111
out:
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000

in:
1011010001111010
1111111011101101
1110010101001011
1111001110010010
1111010000101001
0111101001000101
0011100111110010
1001110011111110
0101000011100011
1110110101001110
0010100111000110
1000110111011010
0100101000100101
0001010101100011
1001010000111101
1000111011000010
out:
0000000000111010
1011110001001000
0000000000000011
0000000100010000
0000010000101000
0000001000000100
0000000011000000
1001100000011110
0000000001000010
0110100001000110
0000100101000010
1000100000000000
0100001000000100
0000000100100001
0000010000111000
0000010000000010
`.match(/\d[10\n]+\d/g);
for(i = 0; test[2*i]; ++i)
{
   input = test[2*i]
   check = test[2*i+1]
   result = F(input)
   ok = check == result
   console.log('Test '+ i + ' ' + (ok?'OK':'FAIL'),
   '\n'+input, '\n'+result)
}

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.