Bao nhiêu lỗ?


17

Thử thách

Đưa ra một đầu vào đồ họa của một hình dạng, xác định có bao nhiêu lỗ trong đó.

Không trùng lặp

Câu hỏi này được đánh dấu là một bản sao có thể có của Quần đảo Count . Tôi tin rằng thử thách này khác với thử thách Đảo Count bởi vì trong thử thách này, bạn phải tìm ra cách loại bỏ các khối chạm vào đường viền.

Đầu vào

Đầu vào sẽ được cung cấp dưới dạng một số dạng đầu vào 2D, hoặc một chuỗi nhiều dòng, một chuỗi các chuỗi hoặc một mảng các mảng ký tự. Điều này đại diện cho hình dạng. Hình dạng được đảm bảo chỉ trong một mảnh, được kết nối bởi cạnh. Vui lòng xác định cách bạn muốn đầu vào được thực hiện.

Đầu ra

Đầu ra là một số nguyên duy nhất cho biết có bao nhiêu lỗ trong hình. Một dòng mới theo dõi được cho phép, nhưng không có khoảng trắng hàng đầu hoặc dấu khác. Nói cách khác, đầu ra phải khớp với biểu thức chính quy ^\d+\n?$.

Một cái lỗ là gì?

Đây là những lỗ đơn:

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

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

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

Đây không phải là lỗ:

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

###
#  
###

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

Khá nhiều, nếu nó khoảng cách tham gia cạnh bên ngoài, nó không phải là một lỗ.

Các trường hợp thử nghiệm

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

Bạn có thể sử dụng bất kỳ ký tự nào thay cho '#' và thay cho các khoảng trắng.

Tiêu chí chấm điểm khách quan

Điểm số được đưa ra là số byte trong chương trình của bạn.

Chiến thắng

Người chiến thắng sẽ là người nộp bài với số điểm thấp nhất, trước ngày 4 tháng Tư.



2
Bạn có thể thêm ###|# #|## như một trường hợp thử nghiệm? Điều đó nên 0, phải không?
Martin Ender


1
Bản sao có thể có của Code-Golf: Count Islands
Matthew Roh

@SIGSEGV Cảm ơn bạn đã chỉ ra điều đó; tuy nhiên, tôi tin rằng thử thách này có một thành phần quan trọng khiến nó đủ khác biệt với thử thách khác để đảm bảo bài đăng của chính nó (tôi đã chỉnh sửa theo cách khác). Xin vui lòng cho tôi biết những gì bạn nghĩ, và chúng tôi có thể muốn bắt đầu một cuộc thảo luận trong trò chuyện nếu cần thiết.
HyperNeutrino

Câu trả lời:


12

MATLAB / Octave, 18 byte

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

Hãy thử trực tuyến!

Đây là một hàm ẩn danh lấy một ma trận logic làm đầu vào. Đối tượng được hình thành bởi các truemục (với kết nối được chỉ định), không gian trống là các falsemục.

bweuler sau đó tính số euler của hình ảnh nhị phân được biểu thị bởi ma trận đó, đó là số lượng đối tượng trừ đi số lượng lỗ.


8

Toán học, 59 57 byte

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

Có tích hợp sẵn cho việc đó. Đưa đầu vào dưới dạng ma trận 2D gồm 1s (tường) và 0s (lỗ). Để thuận tiện, đây là tất cả các trường hợp thử nghiệm ở định dạng đầu vào này:

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

Giải pháp thay thế, 59 byte

Đây là cách tiếp cận ban đầu của tôi. Nó cũng dựa trên các phần tử tích hợp liên quan đến thành phần, nhưng không tính các lỗ trực tiếp (thay vào đó, nó tự xử lý các lỗ đó như các thành phần).

Max@*MorphologicalComponents@*DeleteBorderComponents@*Image

Có định dạng đầu vào giống như trên, nhưng với vai trò của 0s và 1s được hoán đổi.

Lý do tôi cần chuyển đổi nó thành Imageđầu tiên là vì, nếu không, Mathicala sẽ coi tất cả các 1-cell là một phần của một thành phần duy nhất (vì nó coi ma trận là ma trận nhãn thành phần). Do đó, nếu bất kỳ 1-cell giáp với lề, nó sẽ xóa tất cả chúng. Khi DeleteBorderComponentsđược sử dụng trên một hình ảnh thay thế, sau đó nó sẽ thực hiện kiểm tra kết nối ngầm để tìm các thành phần.

Ngoài ra, tôi có thể gọi MorphologicalComponents trước , điều này sẽ biến đầu vào thành ma trận nhãn phù hợp, nhưng nếu tôi thực hiện DeleteBorderComponentslần thứ hai thì không còn đảm bảo rằng nhãn thành phần tối đa tương ứng với số lượng thành phần (vì tôi có thể xóa một thành phần nhỏ hơn).


5
Thực sự, Mathematica đã tích hợp sẵn mọi thứ ...
Ông Xcoder

3
@ Mr.Xcoder Tôi có một ý tưởng thử thách hay: Tìm một thử thách mà Mathematica không có nội dung.
HyperNeutrino

@HyperNeutrino ý tưởng tốt, nhưng tôi nghĩ rằng những người dùng Mathicala sẽ đánh giá thấp nó, thật không may, và tôi không biết liệu cộng đồng có phản ứng tốt không ... =]
Ông Xcoder vào

1
@HyperNeutrino, có lẽ cũng có một bản dựng sẵn cho điều đó :-)
Brian Minton

@BrianMinton Haha. Có lẽ có một tích hợp trong Mathicala được gọi GenerateBuiltin. Nó tạo ra một tích hợp cho bất kỳ thử thách nào không có tích hợp. Ngoài ra, tôi cảm thấy tệ cho hộp thư đến của Martin Ender, vì vậy nếu bạn muốn, hãy tiếp tục cuộc thảo luận này tại đây
HyperNeutrino

4

Perl 5 , 154 byte

152 byte mã + 2 byte cho -p0cờ.

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

Hãy thử trực tuyến!

Tôi nghĩ rằng mã là khá tự giải thích.


Nếu bạn cần một số giải thích để hiểu, đây là một vài bước chuyển đổi được thực hiện bởi chương trình trên một đầu vào đơn giản (đến từ đây ), tiếp theo là một số giải thích dưới đây:

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

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

######
#AAAAA
#A ####
# A # #
#### #
######

######
#AAAAA
#A ####
#CÂY RÌU #
#### #
######

######
#AAAAA
#A ####
# A # XX #
#### X #
######

Đầu tiên, s/^ | $/A/gm;s/^.*\K | (?=.*$)/A/&&redosẽ thay thế các khoảng trắng trong đường viền (regex thứ nhất cho trái / phải, thứ 2 cho dưới cùng / trên cùng) bằng A(tôi chọn ký tự đó khá tùy ý).
Sau đó, chúng ta có được chiều rộng hình dạng với /.*/;$@="@+"-1;.
Bây giờ, chúng tôi muốn thay thế mọi không gian được kết nối Avới a A(vì nếu một không gian được kết nối với a A, điều đó có nghĩa là nó không phải là một phần của lỗ. Điều đó được thực hiện bởi for$%(A,X){(s/$%(.?.?.{$@})? /$%$1$%/s||s/ (.?.?.{$@})?$%/$%$1$%/s)&&redo}(bạn sẽ nhận thấy rằng điều này đã được thực hiện một lần cho As và một cho Xs - giải thích cho bên Xdưới). Có hai regex ở đây: s/$%(.?.?.{$@})? /$%$1$%/sxử lý các khoảng trắng ở bên phải hoặc dưới cùng của a A. Và s/ (.?.?.{$@})?$%/$%$1$%/svới các khoảng trắng ở trên hoặc bên trái của a A.
Tại thời điểm này, các khoảng trống duy nhất còn lại trong chuỗi là một phần của các lỗ.
Trong khi vẫn còn khoảng trống, chúng tôi lặp lại:
- Để biết có bao nhiêu lỗ, chúng tôi thay thế một khoảng trắng bằng X( s/ /X/) và tăng bộ đếm lỗ ( $\++) và làm lại toàn bộ chương trình (thực ra, chúng tôi chỉ muốn làm lại forvòng lặp , nhưng nó ít byte hơn để làm lại toàn bộ chương trình).
- Sau đó, forvòng lặp sẽ thay thế mọi không gian được kết nối với a Xbằng a X, vì chúng là một phần của cùng một lỗ.
Cuối cùng, $\|=0đảm bảo rằng nếu không có lỗ, a 0được in thay vì một chuỗi trống. Và $\được in ngầm nhờ -pcờ.


4

Python 2, 282 byte

+100 để xử lý các đường chéo chạm TT_TT (chúng ta có thực sự cần điều đó không?)
-119 nhờ vào hướng dẫn @Rod :)

Hãy thử trực tuyến . Lấy mảng các mảng ký tự '#' và khoảng trắng làm đầu vào.

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

Tìm kiếm khoảng trắng đầu tiên và đánh dấu nó là không trống ('#'). Kiểm tra đệ quy tất cả các xung quanh, trong khi điền vào tất cả các ô trống. Nếu bất kỳ ô trống nào của "lỗ" hiện tại xuất hiện trên bộ đếm biên sẽ không thay đổi, nếu không nó sẽ tăng thêm 1. Lặp lại quy trình, cho đến khi không còn khoảng trắng nữa.


1
Bạn có thể sử dụng sum(A,[])để làm phẳng
Rod

1
Ngoài ra, bạn có thể kiểm tra câu trả lời này , nó có logic đệ quy tương tự và cũng có một số thủ thuật khác (như chức năng "đổi tên" trong dòng đầu tiên)
Rod

@Rod Trick với sum rất tốt, cảm ơn bạn. Bây giờ tôi đang làm việc để có được tất cả 8 hướng mà không có sự xấu xí này, câu trả lời của bạn có thể giúp ích. Tôi sẽ cập nhật sau đó
Dead Possum

Lưu ý rằng trong câu trả lời của tôi, tôi đã gọi hàm đệ quy bên trong việc hiểu danh sách chỉ để sử dụng ít byte hơn, nhưng trong trường hợp của bạn, bạn có thể kiểm tra độ dài danh sách để xem liệu ô hiện tại có thuộc biên giới hay không (nội dung của danh sách sẽ có rất nhiều Nones, nhưng điều đó không liên quan)
Rod

1
Bạn có thể sử dụng danh sách giải nén trên x=T[0];y=T[1]-> x,y=T, bạn (có thể) không cần phải khai báo g=1trên dòng thứ 3 và bạn có thể sử dụng <hoặc >so sánh các chuỗi (nó sẽ lấy ord()giá trị của mỗi char) cho phép bạn thay thế A[y][x]!='#'bằng A[y][x]<'#', kể từ đó ' '<'#'.
Rod

3

Python 2, 233 225 222 byte

math_junkie : -8 byte

Lấy mảng 2d của booleans / số nguyên (0/1) làm đầu vào

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

Hãy thử trực tuyến!

Phiên bản được định dạng:

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
Bạn có thể lưu một vài byte print sum(m(x,y)...thay vì a=print a
nghiện toán học

1
Ngoài ra, một số sân golf nhỏ: TIO
nghiện toán học

1

C # 7, 364 byte

Ít hài lòng hơn với điều này ... có lẽ người khác có thể sắp xếp nó ... Nếu tôi có năng lượng sau này, tôi sẽ đảo ngược vòng lặp đầu tiên, và xem liệu điều đó có thể giúp kiểm tra giới hạn kiểm tra hay không.

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

Dùng thử trực tuyến

Hoàn thành chương trình, chấp nhận đầu vào theo tiêu chuẩn, đầu ra thành tiêu chuẩn. Sử dụng các bộ khác nhau để xác định các lỗ tạm thời và khi giết bất kỳ chạm vào đường viền (với một chút tinh ranh cho cạnh trên).

Mã định dạng và nhận xét:

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

Chuyển đổi nó thành một Func<List<string>, int>để loại bỏ lông tơ và công cụ điều khiển. Tuy nhiên, tôi đã thấy bạn có các chức năng cục bộ nên bạn không thể có chúng trong một chức năng. Chỉ có thể biên dịch thành một phương thức int h(string[] s) { }.
TheLethalCoder

Tôi chắc chắn có nhiều hơn nữa có thể được đơn giản hóa ở đây ...
TheLethalCoder

@TheLethalCoder Tôi sẽ không chuyển đổi nó sang một hình thức khác, tôi không thích câu trả lời là các hàm (sẽ không cần phải là lambda, như bạn đã nói). Vâng ... toàn bộ điều này cảm thấy bực bội ... nhưng tôi đã chi tiêu tốt trong khi biến đổi nó và không đạt được tiến bộ đáng kể nào, vì vậy tôi đã thực hiện một vài lần chơi golf 'bitty' và đẩy nó. Vui lòng gửi phiên bản ngắn hơn, tôi ít gắn bó với phiên bản này.
VisualMelon

Ý tôi là chỉ cần chuyển đổi nó thành một phương thức và loại bỏ tất cả các công cụ điều khiển, vì điều đó sẽ không còn cần thiết nữa, sẽ loại bỏ 50 - 100 byte (chỉ là một phỏng đoán nhưng nó sẽ đánh bại rất nhiều).
TheLethalCoder

@TheLethalCoder thực sự; Tôi chỉ không thích gửi chức năng như câu trả lời. Đầu vào tiêu chuẩn khá Tiêu chuẩn và 'chương trình hoàn chỉnh' dễ dàng biên dịch và chạy mọi nơi. Đừng để tôi bắt đầu với lambdas chưa được đánh dấu ... Rõ ràng, nếu có câu trả lời Java cạnh tranh, thì tôi sẽ phải
giảm bớt

1

Ốc , 48 byte

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

Ung dung:

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

0

JavaScript (ES6), 192 byte

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>

Dựa trên câu trả lời của tôi để phát hiện các lâu đài không thành công . Đầu tiên, chuỗi được đệm với các khoảng trống để tạo một vùng xung quanh hình. Sau đó, RegExp tìm kiếm hai hình vuông liền kề, một hình chứa một @, một hình chứa một khoảng trắng và thay thế cả hai bằng một hình vuông @. Nếu nó không thể làm điều này, nó sẽ lấp đầy trong một không gian với một @và tính đây là một lỗ mới. Cuối cùng, một cái được trừ vào tài khoản cho khu vực xung quanh.


Bạn có thể cung cấp một liên kết TIO của một số loại? Cảm ơn!
HyperNeutrino
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.