有多少个孔?


17

挑战

给定形状的图形输入,确定其中有多少个孔。

不重复

该问题被标记为Count Islands可能重复。我认为这一挑战与Count Island挑战不同,因为在这一挑战中,您必须弄清楚如何消除接触边界的障碍物。

输入项

输入将以2D输入形式给出,即多行字符串,字符串数组或字符数组数组。这代表形状。保证形状只有一件,通过边缘连接。请指定您希望如何输入。

输出量

输出是一个单个整数,说明形状中有多少个孔。尾随换行符是允许的,但不允许其他前导或尾随空格。换句话说,输出必须匹配正则表达式^\d+\n?$

什么是孔?

这些是单孔:

####
#  #
#  #
####

####
#  #
# ##
###

#####
# # #
#   #
#####

这些不是漏洞:

########
########
#   ####
#   ####
# ######
#       
########

###
#  
###

##########
#         
# ########
# #      #
# # #### #
# #   ## #
# ###### #
#        #
##########

几乎可以说,如果间隙连接到外部边缘,则它不是孔。

测试用例

#####
# # # -> 2
#####

#####
#    
# ### -> 1
# # #
#####

####
## # -> 1 (things are connected by edges)
# ##
####

###
### -> 0 (You must handle shapes with no holes, but input will always contain at least one filled space)
###

您可以使用任何字符代替“#”和空格。

客观评分标准

分数以程序中的字节数给出。

获奖

获胜者将是4月4日之前得分最低的作品。



2
您可以添加###|# #|## 为测试用例吗?应该是0吧?
Martin Ender



@SIGSEGV感谢您指出这一点;但是,我认为,这一挑战具有至关重要的组成部分,使其与其他挑战有足够的差异,因此可以保证自己的职位(我在差异中进行了编辑)。请让我知道您的想法,如有必要,我们可能希望通过聊天开始讨论。
HyperNeutrino

Answers:


12

MATLAB /八度,18字节

@(g)1-bweuler(g,4)

在线尝试!

这是一个匿名函数,将逻辑矩阵作为输入。对象由true条目(具有指定的连通性)形成,空白是false条目。

bweuler 然后计算该矩阵表示的二值图像的欧拉数,即对象数减去孔数。


8

Mathematica,59 57字节

1/.ComponentMeasurements[#,"Holes",CornerNeighbors->0>1]&

有一个内置的。将输入作为1s(壁)和0s(孔)的2D矩阵。为了方便起见,以下是此输入格式的所有测试用例:

{{{1,1,1,1},{1,0,0,1},{1,0,0,1},{1,1,1,1}},
 {{1,1,1,1},{1,0,0,1},{1,0,1,1},{1,1,1,0}},
 {{1,1,1,1,1},{1,0,1,0,1},{1,0,0,0,1},{1,1,1,1,1}},
 {{1,1,1,1,1,1,1,1},{1,1,1,1,1,1,1,1},{1,0,0,0,1,1,1,1},{1,0,0,0,1,1,1,1},{1,0,1,1,1,1,1,1},{1,0,0,0,0,0,0,0},{1,1,1,1,1,1,1,1}},
 {{1,1,1},{1,0,0},{1,1,1}},
 {{1,1,1,1,1,1,1,1,1,1},{1,0,0,0,0,0,0,0,0,0},{1,0,1,1,1,1,1,1,1,1},{1,0,1,0,0,0,0,0,0,1},{1,0,1,0,1,1,1,1,0,1},{1,0,1,0,0,0,1,1,0,1},{1,0,1,1,1,1,1,1,0,1},{1,0,0,0,0,0,0,0,0,1},{1,1,1,1,1,1,1,1,1,1}},
 {{1,1,1,1,1},{1,0,1,0,1},{1,1,1,1,1}},
 {{1,1,1,1,1},{1,0,0,0,0},{1,0,1,1,1},{1,0,1,0,1},{1,1,1,1,1}},
 {{1,1,1,1},{1,1,0,1},{1,0,1,1},{1,1,1,1}}}

替代解决方案,59字节

这是我最初的方法。它也基于与组件相关的内置组件,但不直接计算孔(而是将孔本身视为组件)。

Max@*MorphologicalComponents@*DeleteBorderComponents@*Image

采用与上述相同的输入格式,但互换了0s和1s 的角色。

我需要将其转换为Image第一个的原因是,否则,Mathematica会将所有1-cell视为单个组件的一部分(因为它将矩阵视为组件标签矩阵)。因此,如果有任何1-cell边界,它将删除所有这些。当DeleteBorderComponents在图像上使用的,而不是,那么它会做一个隐式连接检查发现的组件。

或者,我可以MorphologicalComponents 调用,这会将输入转换为合适的标签矩阵,但是如果我DeleteBorderComponents第二次调用,则不再保证最大的组件标签对应于组件的数量(因为我可能会删除一个较小的组件)。


5
确实,Mathematica的所有功能都已内置...
Xcoder先生17年

3
@ Mr.Xcoder我有一个很好的挑战想法:找到Mathematica没有内置的挑战。
HyperNeutrino

@HyperNeutrino是个好主意,但不幸的是,我认为Mathematica的用户会投票否决,而且我不知道社区是否会做出很好的
回应

1
@HyperNeutrino,也可能有内置功能:-)
Brian Minton

@BrianMinton哈哈。Mathematica中可能有一个内置的名为GenerateBuiltin。它为没有内置功能的任何挑战生成内置功能。另外,对于Martin Ender的收件箱,我感到很难过,所以如果您愿意,让我们在这里
HyperNeutrino

4

Perl 5,154个字节

152个字节的代码+ 2个字节的-p0标志。

s/^ | $/A/gm;s/^.*\K | (?=.*$)/A/&&redo;/.*/;$@="@+"-1;for$%(A,X){$~="(.?.?.{$@})?";(s/$%$~ /$%$1$%/s||s/ $~$%/$%$1$%/s)&&redo}s/ /X/&&++$\&&redo}{$\|=0

在线尝试!

我认为代码是不言自明的。


如果您需要一些解释来理解,这是程序在一个简单的输入上完成的转换的几个步骤(来自here),下面是一些解释:

######
#     
#####
###
#####
######

######
# 一种
#####
###
#####
######

######
#AAAAA
#一种####
#一种# #
#####
######

######
#AAAAA
#一种####
#A#X#
#####
######

######
#AAAAA
#一种####
#A#XX#
####X#
######

首先,s/^ | $/A/gm;s/^.*\K | (?=.*$)/A/&&redo将边框中的空格替换为(左/右为第一个正则表达式,底部/顶部为第二个正则表达式)A(我选择该字符相当随意)。
然后,使用来获得形状的宽度/.*/;$@="@+"-1;
现在,我们要用a替换每个A与a A相连的空间(因为如果一个空间连接到a A,则意味着它不能成为孔的一部分。这是通过for$%(A,X){(s/$%(.?.?.{$@})? /$%$1$%/s||s/ (.?.?.{$@})?$%/$%$1$%/s)&&redo}。完成的(您会注意到,这已经完成了曾经为AS和一个用于X秒-对于解释X是波纹管),这里有两个正则表达式:s/$%(.?.?.{$@})? /$%$1$%/s与上的右侧或底部的空间交易A和。s/ (.?.?.{$@})?$%/$%$1$%/s在顶部空间或留下的A
此时,字符串中仅剩下的空格是孔的一部分。
在仍有空格的情况下,我们重复:
-要知道有多少孔,我们用Xs/ /X/)替换空格,并增加()的计数器$\++,然后重做整个程序(实际上,我们只想重做for循环,但重做整个程序所需的字节数更少)。
-然后,for环路将取代每个被连接到一个空间XX,因为它们是相同的孔的一部分。
最后,$\|=0确保如果没有孔,0则打印a而不是空字符串。并$\通过-pflag 隐式打印。


4

Python 2,282字节

+100可处理对角线TT_TT(我们真的需要吗?)
-119感谢@Rod指导:)

在线尝试。将字符数组“#”和空格作为输入数组。

A=input()
c=0
X=len(A[0])-1
Y=len(A)-1
def C(T):
 x,y=T
 global g
 if A[y][x]<'#':
    if y<1or y==Y or x<1or x==X:g=0
    A[y][x]='#';map(C,zip([x]*3+[min(x+1,X)]*3+[max(x-1,0)]*3,[y,min(y+1,Y),max(y-1,0)]*3))
while' 'in sum(A,[]):i=sum(A,[]).index(' ');g=1;C((i%-~X,i/-~X));c+=g
print c

搜索第一个空格并将其标记为非空('#')。递归检查所有周围环境,同时填充所有空单元格。如果当前“空”的任何空白单元格似乎不在边界计数器上,则不会更改,否则将增加1。重复该过程,直到没有更多的空格为止。


1
您可以sum(A,[])用来展平
Rod

1
您也可以检查该答案,它具有相同的递归逻辑,并且还具有其他技巧(例如第一行中的“重新命名”功能)
Rod

@Rod Trick的总和非常好,谢谢。我现在正在努力在没有所有这些丑陋的情况下获得所有8个方向,您的回答可能会有所帮助。之后,我将更新
Dead Possum

请注意,在我的回答中,我在列表理解内调用了递归函数,只是使用了较少的字节,但是根据您的情况,您可以检查列表长度,以查看当前单元格是否属于边框(列表的内容会很多)。Nones,但这无关紧要)
Rod

1
您可以在x=T[0];y=T[1]-> 上使用列表解压缩x,y=T,(可能)不需要g=1在第三行进行声明,并且可以使用<>比较字符串(它将使用ord()每个char 的值),从而可以替换A[y][x]!='#'A[y][x]<'#'的,因为' '<'#'
罗德

3

Python 2中,233个 225 222字节

math_junkie:-8个字节

将2d布尔/整数(0/1)数组作为输入

s=input()
o=[-1,0,1]
m=lambda x,y:0if x in[-1,len(s[0])]or y in[-1,len(s)]else 1if s[y][x]else(s[y].__setitem__(x,1),all([m(x+a,y+b)for a in o for b in o]))[1]
e=enumerate
print sum(m(x,y)-c for y,l in e(s)for x,c in e(l))

在线尝试!

格式化版本:

s = input()
o = [-1, 0, 1]
m = lambda x,y:
    0 if x in [-1, len(s[0])] or y in [-1, len(s)]
      else
        1 if s[y][x]
          else
            (s[y].__setitem__(x, 1),
             all([m(x + a, y + b) for a in o for b in o]))[1]
e = enumerate
print sum(m(x, y) - c for y, l in e(s) for x, c in e(l))

1
您可以保存几个字节,print sum(m(x,y)...而不是a=print a
数学迷

1
另外,还有一些小型的空白高尔夫球场:TIO
数学迷

1

C#7,364字节

对此感到不满意...也许其他人可以解决...如果我以后有能力,我将反转第一个循环,看看是否可以帮助简化边界检查。

using C=System.Console;class P{static void Main(){string D="",L;int W=0,H=0,z;for(;(L=C.ReadLine())!=null;H+=W=L.Length)D+=L;int[]S=new int[H*9];int Q(int p)=>S[p]<p?Q(S[p]):p;void R(int r)=>S[Q(r+=z)]=S[r]>0?z:0;for(z=H;z-->0;)if(D[z]<33){S[z]=z;R(1);R(W);R(W+1);R(W-1);}for(;++z<H;)S[Q(z)]*=z>H-W-2|z%W<1|z%W>W-2?0:1;for(;W<H;)z+=Q(W)<W++?0:1;C.WriteLine(z-H);}}

在线尝试

完整的程序,接受输入到标准输入,输出到标准输出。使用不相交的集来确定临时漏洞,以及在杀死任何接触边界时(对顶部边缘有些躲避)。

格式化和注释的代码:

using C=System.Console;

class P
{
    static void Main()
    {
        string D="", // the whole map
            L; // initally each line of the map, later each line of output

        // TODO: some of thse might be charable
        int W=0, // width, later position
            H=0, // length (width * height)
            z; // position, later counter

        // read map and width
        for(;(L=C.ReadLine())!=null; // read a line, while we can
                H+=W=L.Length) // record the width, and increment height
            D+=L; // add the line to the map

        // disjoint sets
        int[]S=new int[H*9]; // generousness (relieve some bounds checking)
        // note that S[x] <= x, because we call R with decending values of z

        // returns whatever p points to
        int Q(int p)=>S[p]<p?Q(S[p]):p;
        // points whatever r points to at z if r is empty
        void R(int r)=>S[Q(r+=z)]=S[r]>0?z:0; // note that is never called when z=0

        // fill out disjoint sets
        for(z=H;z-->0;)
            if(D[z]<33) // if cell is empty
            {
                S[z]=z; // point it at itself

                // point the things next  to z at z
                R(1);
                R(W);
                R(W+1);
                R(W-1);
            }

        // zero sets which are against the left, bottom, or right edges
        for(;++z<H;)
            S[Q(z)]*=z>H-W-2|z%W<1|z%W>W-2?0:1; // TODO?: this suggests inverting the first loop (NOTE: would break S[x]<=x)

        // starting from the second row, count all the sets that point to this cell (ignores any non-zeros pointing to first row)
        for(;W<H;)
            z+=Q(W)<W++?0:1;

        C.WriteLine(z-H);
    }
}

将其转换为Func<List<string>, int>可以删除绒毛和控制台内容。但是,我已经看到您具有本地功能,因此您可能无法将它们包含在函数中。可以编译成一个方法int h(string[] s) { }
TheLethalCoder

我相信这里还有很多可以简化的地方
TheLethalCoder

@TheLethalCoder我不会将其转换为其他形式,我不喜欢将答案作为函数使用(不必像您所说的那样是lambda)。是的...整个东西感觉很膨胀...但是我花了很多时间使它变异,并且没有取得任何实质性的进步,所以我做了几次“小小的”打高尔夫球并将其推开。随时提交一个简短的版本,我不喜欢这个版本。
VisualMelon

我的意思是仅将其转换为一种方法并删除所有控制台内容(因为将不再需要)将减少50-100个字节(只是一个猜测,但这会带来很多麻烦)。
TheLethalCoder

@TheLethalCoder确实;我只是不喜欢将函数提交为答案。标准输入是相当标准的,“完整程序”易于编译并在任何地方运行。别让我开始使用无类型的lambda ...显然,如果有一个竞争性的Java答案,那么我就不得不放松一下我的标准……
VisualMelon

1

蜗牛,48个字节

!{\ z`+~}\ {t\ z!.!~=((lu|u.+r)!(.,~},!{t\ z!.!~

取消高尔夫:

!{
    (\   z)+
    ~
}
\ 
{
    t \ 
    z !.!~
    ={
        (lu|u.+r)
        !(.,~)
    }
},
!{
    t \ 
    z !.!~
}

0

JavaScript(ES6),192个字节

v=a=>Math.min(...a=a.map(s=>s.length))==Math.max(...a);
f=(s,t=(u=` `.repeat(w=s.search`
`+1))+`
`+s.replace(/^|$/gm,` `)+`
`+u,v=t.replace(RegExp(`( |@)([^]{${w},${w+2}})?(?!\\1)[ @]`),`@$2@`))=>t!=v?f(s,v):/ /.test(t)?f(s,t.replace(` `,`@`))+1:-1
<textarea id=i rows=10 cols=10></textarea><input type=button value=Count onclick=o.textContent=/^[\s#]+$/.test(i.value)*v(i.value.split`\n`)?f(i.value):`Invalid_Entry`><span id=o>

根据我对“ 检测失败的城堡”的回答。首先,用空格填充字符串以在形状周围形成一个区域。然后,RegExp查找两个相邻的正方形,一个包含一个正方形,@一个包含一个空格,并用替换它们@。如果无法执行此操作,则使用填充一个空格,@并将其视为一个新孔。最后,减去一个以说明周围区域。


您可以提供某种TIO链接吗?谢谢!
HyperNeutrino
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.