迷宫中的切点


13

迷宫以任何方便的格式表示为0(墙壁)和1(可步行空间)的矩阵。每个单元都被视为连接到其4个(或更少)正交邻居。阿连接成分是一组可行走单元中的所有传递地彼此连接的。您的任务是确定切入点 -可行走的单元格,如果将它们变成墙壁,将改变连接的组件数。仅在这些位置输出带有1-s的布尔矩阵。我们的目标是用最少的代码字节做到这一点。

输入矩阵将至少包含3行和3列。它的至少一个单元将是一堵墙,至少一个将是可步行的。您的函数或程序必须能够在一分钟内在TIO上(或在您自己的计算机上,如果TIO不支持该语言)处理下面的任何示例。

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先生18年

1
@HyperNeutrino 是不同的-它是一条(不是顶点),其移除会增加连接的组件的数量
ngn

1
此外,@ HyperNeutrino,子图连接的组件
ngn

1
@Notatree你是对的。我犯了一个错误。现在修复它为时已晚,但我希望它不会破坏乐趣。
ngn

Answers:


3

Stax,40 个字节

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

运行和调试测试用例

该程序将输入作为包含行的空格分隔的字符串。输出格式相同。这是解压后的ascii表示形式。

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

计数岛屿的基本操作是这样的。

  1. 用替换第'1'一个'2'
  2. 正则表达式替换'12|21''22'
  3. 在空格上分割。
  4. 转置矩阵。
  5. 从2.重复,直到重复一个字符串。
  6. 从1.重复,直到'1'字符串中不再有a 。重复次数是孤岛的数目。

{               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字节程序 -此版本使用网格格式输入和输出。


它会在一分钟内在您的计算机上处​​理第二个示例吗?
ngn

@ngn:在Chrome这款中端笔记本电脑上,它在41年代完成了所有三个示例。另外,我只是固定了主链接。我不小心将其设置为较旧的无效版本。
递归

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

Perl 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

这将找到两个不同的字母,它们在水平或垂直方向上彼此相邻,并用替换c。在迷宫中,其路径完全由字母组成,b因为字母相同,所以什么也不会发生,但是只要其中一个字母被另一个字母(例如z)替换,该字母和一个邻居就会被替换,c并且重复应用是用c种子填充连接的组件z

但是,在这种情况下,我不需要完整的洪水填充。我只想填充相邻的一只手臂z,所以第一步之后,我要z消失了。这已经可以用于c$2c替换,但是我后来想从同一点开始沿另一个分支重新开始洪水填充,但我不知道cs中的哪个原本z了。所以我改用

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

b | acb | ccz | a{。因此,在迷宫中,路径由组成,第一步中b的种子将被替换,并且将被替换,这不是字母,也不匹配,因此不会引起进一步的填充。该然而,将保持进一步的洪水填充去和种子的一个邻居手臂得到填补。例如从zbcz{\wc

  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/(这0代替了1旧版本的perl。这是错误的)。不幸的是,如果不匹配,它将给出一个空字符串而不是0。但是,按s:|.: code :seg原样设计的总是返回奇数,因此&/\B/this 进行运算将得到0or 1

剩下的就是遍历整个输入数组,并在每个可行走位置播种z并计数连接的手臂。这很容易做到:

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

唯一的问题是,在不可行走的位置中会保留旧值。因为那里需要0s,这意味着原始输入数组必须0位于不可移动的位置,并且必须0\w原始替换匹配,并会触发泛洪填充。这就是为什么我\pL改用(仅匹配字母)的原因。


2

爪哇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);}

-18个字节感谢@ceilingcat

说明:

在线尝试。

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

在线尝试!

得益于Rod得
-11个字节得益于Lynn得-11个字节


1
F(m,i,j)每个元素使用的时间更短,节省了11个字节
Rod

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)
}

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.