กี่หลุม


17

ท้าทาย

กำหนดอินพุตกราฟิกของรูปร่างพิจารณาจำนวนหลุมที่มีอยู่ในนั้น

ไม่ซ้ำ

คำถามนี้ถูกทำเครื่องหมายว่าซ้ำกันเป็นไปได้ของหมู่เกาะจำนวน ฉันเชื่อว่าความท้าทายนี้แตกต่างจากการท้าทาย 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 เมษายน



2
คุณสามารถเพิ่ม###|# #|## เป็นกรณีทดสอบได้หรือไม่? มันควรจะเป็นอย่าง0นั้นใช่ไหม
Martin Ender


1
ซ้ำซ้อนที่เป็นไปได้ของCode-Golf: Count Islands
Matthew Roh

@SIGSEGV ขอบคุณที่ชี้ให้เห็น; อย่างไรก็ตามฉันเชื่อว่าความท้าทายนี้มีองค์ประกอบที่สำคัญที่ทำให้แตกต่างจากความท้าทายอื่น ๆ เพื่อรับประกันการโพสต์ของตัวเอง (ฉันแก้ไขในความแตกต่าง) โปรดแจ้งให้เราทราบว่าคุณคิดอย่างไรและเราอาจต้องการเริ่มการสนทนาในการแชทหากจำเป็น
HyperNeutrino

คำตอบ:


12

MATLAB / Octave, 18 ไบต์

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

ลองออนไลน์!

นี่คือฟังก์ชั่นที่ไม่ระบุชื่อโดยใช้เมทริกซ์เชิงตรรกะเป็นอินพุต วัตถุถูกสร้างขึ้นโดยtrueรายการ (ด้วยการเชื่อมต่อที่ระบุ) พื้นที่ว่างเป็นfalseรายการ

bweuler จากนั้นคำนวณจำนวนออยเลอร์ของอิมเมจไบนารีที่แทนด้วยเมทริกซ์นั้นนั่นคือจำนวนของวัตถุลบด้วยจำนวนรู


8

Mathematica, 59 57 ไบต์

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

มีในตัวสำหรับสิ่งนั้น รับอินพุตเป็นเมทริกซ์ 2 มิติของ1s (ผนัง) และ0s (หลุม) เพื่อความสะดวกนี่คือกรณีทดสอบทั้งหมดในรูปแบบอินพุตนี้:

{{{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 ไบต์

นี่เป็นวิธีดั้งเดิมของฉัน มันยังขึ้นอยู่กับ built-in ที่เกี่ยวข้องกับส่วนประกอบ แต่ไม่นับจำนวนรูโดยตรง (แต่จะถือว่ารูเป็นส่วนประกอบ)

Max@*MorphologicalComponents@*DeleteBorderComponents@*Image

ใช้รูปแบบอินพุตเดียวกันกับด้านบน แต่ด้วยบทบาทของ0s และ1s สลับกัน

เหตุผลที่ฉันต้องแปลงสิ่งนี้เป็นอันImageแรกก็คือมิฉะนั้นมา1เธมาติก้าจะพิจารณา-cells ทั้งหมดเป็นส่วนหนึ่งขององค์ประกอบเดียว (เพราะมันถือว่าเมทริกซ์เป็นเมทริกซ์คอมโพเนนต์ของฉลาก) ดังนั้นหาก1-cell มีระยะขอบมันจะลบทั้งหมด เมื่อDeleteBorderComponentsใช้กับรูปภาพแทนจะทำการตรวจสอบการเชื่อมต่อโดยนัยเพื่อค้นหาส่วนประกอบ

อีกทางหนึ่งฉันสามารถโทรMorphologicalComponents ก่อนซึ่งจะเปลี่ยนอินพุตให้เป็นเมทริกซ์ฉลากที่เหมาะสม แต่ถ้าฉันทำอย่างDeleteBorderComponentsที่สองก็ไม่รับประกันว่าฉลากคอมโพเนนต์สูงสุดจะสอดคล้องกับจำนวนขององค์ประกอบ (เพราะฉันอาจลบองค์ประกอบที่เล็กลง)


5
จริงๆ Mathematica ได้มี built-ins สำหรับทุกอย่างเพียง ...
นาย Xcoder

3
@ Mr.Xcoder ฉันมีความคิดที่ท้าทาย: หาสิ่งที่ Mathematica ไม่มีในตัว
HyperNeutrino

@HyperNeutrino ความคิดที่ดี แต่ฉันคิดว่าผู้ใช้ Mathematica จะลงคะแนนอย่างหนักโชคไม่ดีและฉันไม่รู้ว่าชุมชนจะตอบสนองได้ดีหรือไม่ ... =]
Mr. Xcoder

1
@HyperNeutrino อาจมี builtin สำหรับนั่นเกินไป :-)
Brian Minton

@BrianMinton Haha อาจมีในตัวใน 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

ลองออนไลน์!

ฉันคิดว่ารหัสค่อนข้างอธิบายตนเอง


หากคุณต้องการคำอธิบายเพื่อทำความเข้าใจนี่คือขั้นตอนบางขั้นตอนของการแปลงที่ทำโดยโปรแกรมในอินพุตแบบง่าย (มาจากที่นี่ ) ตามด้วยคำอธิบายการร้อง:

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

######
# A
# ####
# # #
#### #
######

######
#AAAAA
รุ่น A ####
# A # #
#### #
######

######
#AAAAA
รุ่น A ####
# A # X #
#### #
######

######
#AAAAA
รุ่น A ####
# A # XX #
#### X #
######

ก่อนอื่นs/^ | $/A/gm;s/^.*\K | (?=.*$)/A/&&redoจะแทนที่ช่องว่างในเส้นขอบ (ลำดับที่ 1 สำหรับซ้าย / ขวาอันดับที่ 2 สำหรับด้านล่าง / ด้านบน) ด้วยA(ฉันเลือกตัวละครนั้นโดยพลการ) จากนั้นเราจะได้รับความกว้างของรูปร่างด้วย
ตอนนี้เราต้องการแทนที่ทุกช่องว่างที่เชื่อมต่อกับ a ด้วย(เพราะถ้าช่องว่างเชื่อมต่อกับมันหมายความว่ามันไม่สามารถเป็นส่วนหนึ่งของรูที่ทำโดย(คุณจะสังเกตเห็นว่าสิ่งนี้เสร็จสิ้นแล้ว หนึ่งครั้งสำหรับs และอีกหนึ่งสำหรับs - คำอธิบายสำหรับการร้อง) มีสอง regex ที่นี่: จัดการกับช่องว่างที่อยู่ทางขวาหรือด้านล่างของ a และมีช่องว่างที่ด้านบนหรือด้านซ้ายของ/.*/;$@="@+"-1;
AAAfor$%(A,X){(s/$%(.?.?.{$@})? /$%$1$%/s||s/ (.?.?.{$@})?$%/$%$1$%/s)&&redo}AXXs/$%(.?.?.{$@})? /$%$1$%/sAs/ (.?.?.{$@})?$%/$%$1$%/sAมีช่องว่างด้านบนหรือด้านซ้ายของ
ณ จุดนี้ช่องว่างเท่านั้นที่เหลืออยู่ในสตริงเป็นส่วนหนึ่งของหลุม
ในขณะที่ยังคงมีช่องว่างเราทำซ้ำ:
- เพื่อทราบจำนวนหลุมที่มีเราเปลี่ยนช่องว่างด้วยX( s/ /X/) และเพิ่มตัวนับจำนวนหลุม ( $\++) และเพิ่มโปรแกรมทั้งหมดใหม่ (จริง ๆ แล้วเราต้องการทำซ้ำforลูปเท่านั้น แต่น้อยกว่าไบต์ที่จะทำซ้ำโปรแกรมทั้งหมด)
- จากนั้นforลูปจะแทนที่ทุกช่องว่างที่เชื่อมต่อXกับ a Xเนื่องจากเป็นส่วนหนึ่งของรูเดียวกัน
ในตอนท้าย$\|=0ตรวจสอบให้แน่ใจว่าหากไม่มีรูใด ๆ a 0จะถูกพิมพ์แทนที่จะเป็นสตริงว่าง และ$\พิมพ์โดยปริยายต้องขอบคุณ-pธง


4

Python 2, 282 ไบต์

+100 เพื่อจัดการกับการแตะในแนวทแยง TT_TT (เราต้องการมันจริงๆหรือเปล่า?)
-119 ขอบคุณ @Rod guide :)

ลองมันออนไลน์ ใช้อาร์เรย์อาร์เรย์ของตัวอักษร '#' และช่องว่างเป็นอินพุต

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

@ เคล็ดลับกับผลรวมที่ดีมากขอบคุณ ตอนนี้ฉันกำลังพยายามหาทิศทางทั้ง 8 โดยไม่มีความอัปลักษณ์ทั้งหมดคำตอบของคุณอาจช่วยได้ ฉันจะอัปเดตหลังจากนั้น
Dead Possum

โปรดทราบว่าในคำตอบของฉันฉันเรียกว่าฟังก์ชั่นวนซ้ำภายในความเข้าใจของรายการเพื่อใช้ไบต์น้อยลง แต่ในกรณีของคุณคุณสามารถตรวจสอบความยาวของรายการเพื่อดูว่าเซลล์ปัจจุบันเป็นของเส้นขอบหรือไม่Nones แต่นั่นไม่เกี่ยวข้องเลย)
Rod

1
คุณสามารถใช้รายการเอาออกบนx=T[0];y=T[1]-> x,y=Tคุณ (อาจ) ไม่จำเป็นต้องประกาศg=1ในบรรทัดที่ 3 และคุณสามารถใช้<หรือ>เพื่อเปรียบเทียบสตริง (มันจะใช้ord()ค่าของแต่ละถ่าน) ช่วยให้คุณสามารถแทนที่A[y][x]!='#'ด้วยเนื่องจากA[y][x]<'#' ' '<'#'
Rod

3

Python 2, 233 225 222 ไบต์

math_junkie : -8 ไบต์

รับอาร์เรย์ 2d ของ booleans / จำนวนเต็ม (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
คณิตศาสตร์ junkie

1
นอกจากนี้บางสนามกอล์ฟเล็ก ๆ น้อย ๆ : TIO
คณิตศาสตร์ junkie

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

ลองออนไลน์

โปรแกรมที่สมบูรณ์ยอมรับอินพุตเข้าสู่มาตรฐานส่งออกเป็นมาตรฐาน ใช้ disjoint ตั้งค่าเพื่อหาหลุมชั่วคราวและเมื่อฆ่าใด ๆ ที่สัมผัสกับเส้นขอบ (ด้วยความหลบสำหรับขอบบน)

รูปแบบและรหัสความคิดเห็น:

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

แปลงเป็น a Func<List<string>, int>เพื่อลบสิ่งที่เป็นปุยและคอนโซล อย่างไรก็ตามฉันเห็นว่าคุณมีฟังก์ชั่นในตัวเครื่องดังนั้นคุณอาจไม่สามารถใช้ฟังก์ชั่นเหล่านี้ได้ใน func สามารถคอมไพล์เมธอดint h(string[] s) { }ได้
TheLethalCoder

ฉันแน่ใจว่ามีจำนวนมากอื่น ๆ ที่สามารถจะง่ายที่นี่ ...
TheLethalCoder

@TheLethalCoder ฉันไม่แปลงเป็นรูปแบบอื่นฉันไม่ชอบคำตอบในฐานะฟังก์ชั่น (ไม่จำเป็นต้องเป็นแลมบ์ดาอย่างที่คุณพูด) ใช่ ... สิ่งทั้งหมดรู้สึกปูด ... แต่ฉันใช้เวลาที่ดีในขณะที่การกลายพันธุ์และไม่ได้มีความคืบหน้าอย่างมีนัยสำคัญดังนั้นฉันจึงไม่กี่รอบของการเล่นกอล์ฟ 'bitty' และผลักดันมัน อย่าลังเลที่จะส่งเวอร์ชั่นสั้นกว่าฉันแนบไฟล์นี้น้อยกว่า
VisualMelon

ฉันหมายถึงเพียงแปลงมันเป็นวิธีการและลบทุกสิ่งที่คอนโซลเนื่องจากไม่ต้องการอีกต่อไปแล้วจะลดลง 50-100 ไบท์ (คาดเดา แต่มันจะล้มลงมาก)
TheLethalCoder

@TheLethalCoder แน่นอน; ฉันไม่ชอบการส่งฟังก์ชั่นเป็นคำตอบ อินพุตมาตรฐานค่อนข้างมาตรฐานและ 'โปรแกรมที่สมบูรณ์' นั้นง่ายต่อการรวบรวมและเรียกใช้จากทุกที่ อย่าให้ฉันเริ่มต้นกับlambdas ที่ไม่มีการพิมพ์ ... เห็นได้ชัดว่าถ้ามีคำตอบของจาวาที่แข่งขันกันแล้วฉันจะต้องทำให้มาตรฐานของฉัน
ช้าลง


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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.