Berapa Banyak Lubang?


17

Tantangan

Diberikan input grafis dari suatu bentuk, tentukan berapa banyak lubang yang ada di dalamnya.

Tidak Gandakan

Pertanyaan ini ditandai sebagai duplikat yang mungkin dari Pulau Count . Saya percaya tantangan ini berbeda dari tantangan Count Island karena dalam tantangan ini, Anda harus memikirkan cara menghilangkan blok yang menyentuh perbatasan.

Memasukkan

Input akan diberikan sebagai bentuk input 2D, baik string multiline, array string, atau array array karakter. Ini mewakili bentuk. Bentuknya dijamin hanya akan menjadi satu bagian, dihubungkan dengan ujung. Silakan tentukan bagaimana Anda ingin input diambil.

Keluaran

Output adalah bilangan bulat tunggal yang menyatakan berapa banyak lubang yang ada dalam bentuk. Sebuah trailing newline diizinkan, tetapi tidak ada spasi putih terdepan atau tambahan lainnya. Dengan kata lain, output harus cocok dengan ekspresi reguler ^\d+\n?$.

Apa itu lubang?

Ini adalah lubang tunggal:

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

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

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

Ini bukan lubang:

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

###
#  
###

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

Cukup banyak, jika celah bergabung dengan tepi luar, itu bukan lubang.

Uji kasus

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

Anda dapat menggunakan karakter apa pun sebagai ganti '#', dan sebagai ganti spasi.

Kriteria Penilaian Objektif

Skor diberikan sebagai jumlah byte dalam program Anda.

Kemenangan

Pemenang akan menjadi pengajuan dengan skor terendah, pada 4 April.



2
Bisakah Anda menambahkan ###|# #|## sebagai test case? Seharusnya begitu 0, kan?
Martin Ender


1
Kemungkinan duplikat Code-Golf: Count Islands
Matthew Roh

@SIGSEGV Terima kasih telah menunjukkan itu; Namun, saya percaya bahwa tantangan ini memiliki komponen penting yang membuatnya cukup berbeda dari tantangan lain untuk menjamin posnya sendiri (saya edit dalam perbedaan). Tolong beri tahu saya apa pendapat Anda, dan kami mungkin ingin memulai diskusi dalam obrolan jika perlu.
HyperNeutrino

Jawaban:


12

MATLAB / Oktaf, 18 byte

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

Cobalah online!

Ini adalah fungsi anonim yang mengambil matriks logis sebagai input. Objek dibentuk oleh trueentri (dengan konektivitas yang ditentukan), ruang kosong adalah falseentri.

bweuler kemudian menghitung angka euler dari citra biner yang diwakili oleh matriks itu, yaitu jumlah objek dikurangi jumlah lubang.


8

Mathematica, 59 57 byte

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

Ada built-in untuk itu. Mengambil input sebagai matriks 2D 1s (dinding) dan 0s (lubang). Untuk kenyamanan, berikut adalah semua test case dalam format input ini:

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

Solusi alternatif, 59 byte

Ini adalah pendekatan awal saya. Ini juga didasarkan pada komponen bawaan yang terkait, tetapi tidak menghitung lubang secara langsung (melainkan memperlakukan lubang itu sendiri sebagai komponen).

Max@*MorphologicalComponents@*DeleteBorderComponents@*Image

Mengambil format input yang sama seperti di atas, tetapi dengan peran 0s dan 1s bertukar.

Alasan saya perlu mengonversikan ini menjadi yang Imagepertama adalah, jika tidak, Mathematica akan menganggap semua- 1sel sebagai bagian dari komponen tunggal (karena itu memperlakukan matriks sebagai matriks komponen-label). Oleh karena itu, jika ada - 1sel yang berbatasan dengan margin, itu akan menghapus semuanya. Ketika DeleteBorderComponentsdigunakan pada gambar sebagai gantinya, maka itu akan melakukan pemeriksaan konektivitas implisit untuk menemukan komponen.

Atau, saya bisa menelepon MorphologicalComponents dulu , yang akan mengubah input menjadi matriks label yang sesuai, tetapi jika saya lakukan DeleteBorderComponentskedua itu tidak lagi dijamin bahwa label komponen maksimum sesuai dengan jumlah komponen (karena saya mungkin menghapus komponen yang lebih kecil).


5
Sungguh, Mathematica telah memiliki
bawaan

3
@ Mr.Xcoder Saya punya ide tantangan yang bagus: Temukan tantangan yang tidak dibangun oleh Mathematica.
HyperNeutrino

@HyperNeutrino ide bagus, tapi saya pikir para pengguna Mathematica akan sangat downvote, sayangnya, dan saya tidak tahu apakah komunitas akan bereaksi dengan baik ... =]
Mr. Xcoder

1
@HyperNeutrino, mungkin ada builtin untuk itu juga :-)
Brian Minton

@BrianMinton Haha. Mungkin ada built-in di Mathematica yang disebut GenerateBuiltin. Ini menghasilkan built-in untuk setiap tantangan yang tidak memiliki built-in. Juga, saya merasa tidak enak untuk kotak masuk Martin Ender, jadi jika Anda mau, mari kita lanjutkan diskusi ini di sini
HyperNeutrino

4

Perl 5 , 154 byte

152 byte kode + 2 byte untuk -p0flag.

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

Cobalah online!

Saya pikir kode ini cukup jelas.


Jika Anda memerlukan beberapa penjelasan untuk dipahami, berikut adalah beberapa langkah transformasi yang dilakukan oleh program dengan masukan sederhana (datang dari sini ), diikuti dengan beberapa penjelasan di bawah:

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

######
# SEBUAH
# ####
# # #
#### #
######

######
#AAAAA
#SEBUAH####
#SEBUAH# #
#### #
######

######
#AAAAA
#SEBUAH####
# A # X #
#### #
######

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

Pertama, s/^ | $/A/gm;s/^.*\K | (?=.*$)/A/&&redoakan mengganti spasi di perbatasan (regex ke-1 untuk kiri / kanan, ke-2 untuk ke bawah / atas) dengan A(saya memilih karakter yang cukup sewenang-wenang).
Lalu, kita dapatkan lebar bentuk dengan /.*/;$@="@+"-1;.
Sekarang, kami ingin mengganti setiap ruang yang terhubung ke Adengan A(karena jika ruang terhubung ke A, itu berarti itu tidak bisa menjadi bagian dari lubang. Itu dilakukan oleh for$%(A,X){(s/$%(.?.?.{$@})? /$%$1$%/s||s/ (.?.?.{$@})?$%/$%$1$%/s)&&redo}. (Anda akan melihat bahwa ini dilakukan sekali untuk As dan satu untuk Xs - penjelasan untuk di Xbawah) .Ada dua regex di sini: s/$%(.?.?.{$@})? /$%$1$%/sberurusan dengan spasi yang ada di kanan atau bawah A. ADan s/ (.?.?.{$@})?$%/$%$1$%/sdengan spasi di atas atau kiri a A.
Pada titik ini, satu-satunya ruang yang tersisa di string adalah bagian dari lubang.
Meskipun masih ada spasi, kami ulangi:
- Untuk mengetahui berapa banyak lubang yang ada, kami mengganti spasi dengan X( s/ /X/) dan menambah penghitung lubang ( $\++), dan mengulang seluruh program (sebenarnya, kami hanya ingin mengulang forloop) , tetapi kurang byte untuk mengulang seluruh program).
- Kemudian, forloop akan mengganti setiap ruang yang terhubung ke Xdengan X, karena mereka adalah bagian dari lubang yang sama.
Pada akhirnya, $\|=0pastikan bahwa jika tidak ada lubang, a 0dicetak dan bukannya string kosong. Dan $\dicetak secara tersirat berkat -pbendera.


4

Python 2, 282 byte

+100 untuk menangani sentuhan diagonal TT_TT (apakah kita benar-benar membutuhkannya?)
-119 berkat panduan @Rod :)

Cobalah online . Mengambil array array karakter '#' dan spasi sebagai input.

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

Mencari spasi putih pertama dan menandainya sebagai non-kosong ('#'). Periksa secara rekursif semua di sekitarnya, sambil mengisi semua sel kosong. Jika sel kosong "lubang" saat ini tampaknya berada di konter perbatasan tidak akan berubah, jika tidak maka akan meningkat 1. Proses ulang, sampai tidak ada lagi spasi putih.


1
Anda dapat menggunakan sum(A,[])untuk meratakan
Rod

1
Anda juga dapat memeriksa jawaban ini , ia memiliki logika rekursif yang sama, dan juga memiliki beberapa trik lain (seperti fungsi "penggantian nama" di baris pertama)
Rod

Trik @Rod dengan jumlah sangat bagus, terima kasih. Saya sekarang bekerja untuk mendapatkan semua 8 arah tanpa semua keburukan ini, jawaban Anda mungkin membantu. Saya akan memperbarui setelah itu
Dead Possum

Perhatikan bahwa pada jawaban saya, saya memanggil fungsi rekursif di dalam daftar pemahaman hanya untuk menggunakan lebih sedikit byte, tetapi pada kasus Anda, Anda dapat memeriksa panjang daftar untuk melihat apakah sel saat ini milik perbatasan (isi daftar akan banyak Nones, tapi itu tidak relevan)
Rod

1
Anda dapat menggunakan daftar membongkar pada x=T[0];y=T[1]-> x,y=T, Anda (mungkin) tidak perlu mendeklarasikan g=1pada baris ke-3, dan Anda dapat menggunakan <atau >membandingkan string (itu akan mengambil ord()nilai dari masing-masing karakter) yang memungkinkan Anda untuk mengganti A[y][x]!='#'dengan A[y][x]<'#', karena ' '<'#'.
Rod

3

Python 2, 233 225 222 byte

math_junkie : -8 byte

Mengambil array 2d dari booleans / integer (0/1) sebagai input

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

Cobalah online!

Versi yang diformat:

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
Anda dapat menyimpan beberapa byte dengan print sum(m(x,y)...sebagai ganti a=danprint a
math junkie

1
Juga, beberapa golf spasi putih kecil: TIO
pecandu matematika

1

C # 7, 364 byte

Kurang senang dengan ini ... mungkin orang lain dapat mengatasinya ... Jika saya memiliki energi nanti saya akan membalikkan loop pertama, dan melihat apakah itu dapat membantu untuk memotong batas memeriksa.

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

Cobalah online

Program yang lengkap, menerima input ke standar, output ke standar Menggunakan set terpisah untuk menentukan lubang sementara, dan ketika membunuh menyentuh batas (dengan beberapa dodgyness untuk tepi atas).

Kode yang diformat dan dikomentari:

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

Konversikan ke a Func<List<string>, int>untuk menghapus bulu dan konsol. Namun, saya telah melihat Anda memiliki fungsi lokal sehingga Anda mungkin tidak dapat memilikinya di func. Hanya bisa mengkompilasi ke suatu metode int h(string[] s) { }.
TheLethalCoder

Saya yakin ada banyak lagi yang dapat disederhanakan di sini ...
TheLethalCoder

@TheLethalCoder Saya tidak akan mengubah ini ke bentuk yang berbeda, saya tidak suka jawaban sebagai fungsi (tidak perlu menjadi lambda, seperti yang Anda katakan). Ya ... semuanya terasa membengkak ... tapi saya menghabiskan waktu dengan baik untuk memutasinya dan tidak membuat kemajuan yang berarti, jadi saya melakukan beberapa operan 'bitty' golf dan mendorongnya. Jangan ragu untuk mengirimkan versi yang lebih pendek, saya kurang terikat dengan yang ini.
VisualMelon

Maksud saya hanya mengubahnya menjadi metode dan menghapus semua hal-hal konsol, karena itu tidak lagi diperlukan, akan mengetuk 50-100 byte (hanya tebakan tetapi itu akan mengetuk banyak).
TheLethalCoder

@TheLethalCoder memang; Saya hanya tidak suka mengirimkan fungsi sebagai jawaban. Input Standar adalah Standar yang cantik, dan 'program lengkap' mudah untuk dikompilasi dan dijalankan di mana saja. Jangan mulai dengan lambda yang tidak diketik ... Jelas, jika ada jawaban Java yang bersaing, maka saya harus sedikit mengendur standar saya ...
VisualMelon

1

Siput , 48 byte

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

Tidak Disatukan:

!{
    (\   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>

Berdasarkan jawaban saya untuk Mendeteksi Kastil Gagal . Pertama-tama tali diisi dengan ruang untuk membuat area di sekitar bentuk. RegExp kemudian mencari dua kotak yang berdekatan, satu berisi @, satu berisi spasi, dan menggantinya dengan @. Jika tidak bisa melakukan ini, ia mengisi ruang dengan @dan menghitung ini sebagai lubang baru. Akhirnya seseorang dikurangkan untuk memperhitungkan daerah sekitarnya.


Bisakah Anda memberikan semacam tautan TIO? Terima kasih!
HyperNeutrino
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.