Kaç Delik?


17

Meydan okuma

Şeklin grafiksel bir girdisi verildiğinde, içinde kaç delik olduğunu belirleyin.

Kopya değil

Bu soru Kont Adaları'nın olası bir kopyası olarak işaretlendi . Bu zorluğun Kont Adası sorunundan farklı olduğuna inanıyorum çünkü bunda, sınıra temas eden blokları nasıl ortadan kaldıracağınızı bulmalısınız.

Giriş

Girdi, çok satırlı bir dize, bir dizi dizi veya bir dizi karakter dizisi olarak bazı 2D giriş formları olarak verilir. Bu şekli temsil eder. Şeklin kenar ile bağlanan tek bir parça halinde olması garanti edilir. Lütfen girdinin nasıl alınmasını istediğinizi belirtin.

Çıktı

Çıktı, şekildeki kaç delik olduğunu belirten tek bir tamsayıdır. Sondaki yeni satıra izin verilir, ancak başka önde gelen veya sondaki boşluk olmaz. Başka bir deyişle, çıktı normal ifadeyle eşleşmelidir ^\d+\n?$.

Delik nedir?

Bunlar tek deliklerdir:

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

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

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

Bunlar delik değil:

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

###
#  
###

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

Hemen hemen, boşluk dış kenara katılırsa, bir delik değildir.

Test senaryoları

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

'#' Yerine ve boşluk yerine herhangi bir karakteri kullanabilirsiniz.

Amaç Puanlama Kriterleri

Skor, programınızdaki bayt sayısı olarak verilir.

Kazanan

Kazanan, 4 Nisan'a kadar en düşük puana sahip sunum olacak.



2
###|# #|## Test örneği olarak ekleyebilir misiniz ? Öyle olmalı 0, değil mi?
Martin Ender



@SIGSEGV Bunu işaret ettiğiniz için teşekkür ederiz; Ancak, bu zorluğun, kendi görevini garanti etmek için diğer zorluktan yeterince farklı kılan kritik bir bileşeni olduğuna inanıyorum (farkı düzenledim). Lütfen ne düşündüğünüzü bana bildirin, gerekirse sohbette bir tartışma başlatmak isteyebiliriz.
HyperNeutrino

Yanıtlar:


12

MATLAB / Oktav, 18 bayt

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

Çevrimiçi deneyin!

Bu, mantıksal bir matrisi girdi olarak alan anonim bir fonksiyondur. Nesne truegirişlerden oluşur (belirtilen bağlantı ile), boş alan falsegirişlerdir.

bweuler daha sonra bu matrisle temsil edilen ikili görüntünün euler sayısını, yani nesne sayısı eksi delik sayısını hesaplar.


8

Mathematica, 59 57 bayt

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

Bunun için bir yerleşik var. Girişi 1s (duvarlar) ve 0s (delikler) 'in 2D matrisi olarak alır . Kolaylık olması açısından, bu giriş biçimindeki tüm test örnekleri şunlardır:

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

Alternatif çözüm, 59 bayt

Bu benim orijinal yaklaşımımdı. Ayrıca bileşenle ilgili yerleşiklere dayanır, ancak delikleri doğrudan saymaz (bunun yerine deliklerin kendilerine bileşen olarak davranır).

Max@*MorphologicalComponents@*DeleteBorderComponents@*Image

Yukarıdaki giriş formatını alır, ancak 0s ve 1s'nin rolleri değiştirilir.

Bunu birinciye dönüştürmemizin nedeni Image, aksi takdirde, Mathematica'nın tüm 1-selleri tek bir bileşenin parçası olarak görmesidir (çünkü matrisi bir bileşen etiketi matrisi olarak kabul eder). Bu nedenle, herhangi bir 1-sel kenar boşluğunu sınırlarsa, hepsini siler. Ne zaman DeleteBorderComponentsyerine resim kullanılan, o zaman bileşenleri bulmak için örtük bir bağlantı çeki yapacağız.

Alternatif olarak, MorphologicalComponents ilk olarak girdiyi uygun bir etiket matrisine dönüştürecek şekilde arayabilirim , ancak DeleteBorderComponentsikincisini yaparsam , maksimum bileşen etiketinin bileşen sayısına karşılık gelmesi artık garanti edilmez (çünkü daha küçük bir bileşeni silebilirim).


5
Gerçekten, Mathematica her şey için yerleşik bir yapıya sahip ...
Bay Xcoder

3
@ Mr.Xcoder İyi bir meydan okuma fikrim var: Mathematica'nın yerleşik olmadığı bir meydan okuma bulun.
HyperNeutrino

@HyperNeutrino iyi fikir, ama Mathematica kullanıcılarının maalesef ağır aşağıya düşeceğini düşünüyorum ve maalesef toplumun iyi tepki verip vermeyeceğini bilmiyorum ... =]
Bay Xcoder

1
@HyperNeutrino, muhtemelen bunun için bir yerleşik var :-)
Brian Minton

@BrianMinton Haha. Muhtemelen Mathematica'da yerleşik bir yerleşik var GenerateBuiltin. Yerleşik olmayan herhangi bir zorluk için yerleşik bir oluşturur. Ayrıca, Martin Ender'in gelen kutusu için kendimi kötü hissediyorum, bu yüzden eğer istersen, bu tartışmaya burada
HyperNeutrino

4

Perl 5 , 154 bayt

152 bayt kod + -p0bayrak için 2 bayt .

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

Çevrimiçi deneyin!

Kodun oldukça açıklayıcı olduğunu düşünüyorum.


Anlamak için bazı açıklamalara ihtiyacınız varsa, program tarafından basit bir girdi ( buradan geliyor ) üzerinde yapılan dönüşümlerin birkaç adımı ve ardından aşağıdaki bazı açıklamalar verilmiştir:

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

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

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

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

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

İlk olarak, s/^ | $/A/gm;s/^.*\K | (?=.*$)/A/&&redokenarlıktaki boşlukları (sol / sağ için 1. regex, alt / üst için 2. regex) ile değiştireceğim A(Bu karakteri oldukça keyfi seçiyorum).
Sonra, şekli ile genişliği elde ediyoruz /.*/;$@="@+"-1;.
Şimdi, a'ya bağlı her alanı bir Aile değiştirmek istiyoruz A(çünkü bir a A, bir alana bağlıysa , bunun bir deliğin parçası olamayacağı anlamına gelir. Bu yapılır for$%(A,X){(s/$%(.?.?.{$@})? /$%$1$%/s||s/ (.?.?.{$@})?$%/$%$1$%/s)&&redo}. ( Bunun yapıldığını fark edeceksiniz) için bir kere As ve diğeri Xs - için açıklamalar X) feryat iki regex burada vardır:. s/$%(.?.?.{$@})? /$%$1$%/sbir sağ veya alt kısmındadır boşluklarla fırsatlar Ave. s/ (.?.?.{$@})?$%/$%$1$%/süstte boşluklu veya sol A.
Bu noktada, dizede kalan tek boşluk deliklerin bir parçasıdır.
Hala boşluklar varken, tekrarlıyoruz:
- Ne kadar delik olduğunu bilmek için, bir boşluğu bir X( s/ /X/) ile değiştiririz ve deliklerin sayacını ( $\++) arttırırız ve tüm programı yeniden yaparız (aslında, sadece fordöngüyü yeniden yapmak istiyoruz) , ancak tüm programı yeniden yapmak daha az bayttır).
- Daha sonra fordöngü , aynı deliğin bir parçası oldukları için a'ya bağlı her boşluğu a Xile değiştirir X.
Sonunda, $\|=0delik yoksa, 0boş bir dize yerine a'nın yazdırılmasını sağlar. Ve $\dolaylı olarak -pbayrak sayesinde basılmıştır .


4

Python 2, 282 bayt

Diyagonal dokunuşları ele almak için +100 TT_TT (gerçekten ihtiyacımız var mı?)
-Rod kılavuzluğu sayesinde -119 :)

Çevrimiçi deneyin . Girdi olarak '#' karakter ve boşluk dizilerini alır.

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

İlk boşlukları arar ve boş olmayan ('#') olarak işaretler. Tüm boş hücreleri doldururken, çevresini tekrar tekrar kontrol edin. Geçerli "delik" boş hücre sınır sayacı üzerinde görünüyorsa, değişmez, aksi takdirde 1 artırılır. Daha fazla boşluk kalmayıncaya kadar işlemi tekrarlayın.


1
sum(A,[])Düzleştirmek için kullanabilirsiniz
Rod

1
Ayrıca bu yanıtı kontrol edebilirsiniz , aynı özyinelemeli mantığa sahiptir ve diğer bazı hilelere de sahiptir (ilk satırda "yeniden adlandırma" işlevi gibi)
Rod

@Rod sum ile çok iyi, teşekkür ederim. Şimdi tüm bu çirkinlik olmadan 8 yön almak için çalışıyorum, cevabınız yardımcı olabilir. Bundan sonra güncelleyeceğim
Dead Possum

Cevabımda, sadece daha az bayt kullanmak için bir liste kavraması içinde özyinelemeli işlevi çağırdığımı unutmayın, ancak durumunuzda, geçerli hücrenin sınırlara ait olup olmadığını görmek için liste uzunluğunu kontrol edebilirsiniz (listenin içeriği çok olacak Nones, ancak bu önemsiz)
Rod

1
Üzerinde açma listeyi kullanabilirsiniz x=T[0];y=T[1]-> x,y=T, (muhtemelen) Eğer bir açıklamadan gerekmez g=13 satırda, ve kullanabileceğiniz <veya >(sürer dizeleri karşılaştırmak için ord()her char değeri) Değiştirmeye izin A[y][x]!='#'ile A[y][x]<'#'beri ' '<'#'.
Rod

3

Piton 2, 233 225 222 bayt

math_junkie : -8 bayt

Giriş olarak 2d dizi booleans / tamsayı (0/1) alır

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

Çevrimiçi deneyin!

Biçimlendirilmiş sürüm:

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
Veprint sum(m(x,y)...a=print a
matematik bayramı

1
Ayrıca, bazı küçük beyaz golfler: TIO
matematik bağımlısı

1

C # 7, 364 bayt

Bundan daha az memnun olabilir ... belki bir başkası bunu çözebilir ... Daha sonra enerjiye sahip olursam ilk döngüyü tersine çeviririm ve bunun sınır kontrolünü düzeltmeye yardımcı olup olamayacağını görürüz.

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

Çevrimiçi deneyin

Komple program, standart girişe giriş, standart çıkışa çıkış kabul eder. Geçici delikleri belirlemek için ayrık kümeler kullanır ve herhangi bir öldürme sınırlara temas ettiğinde (üst kenar için biraz tehlikelilık ile).

Biçimlendirilmiş ve yorumlanmış kod:

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>Tüyleri ve konsol malzemelerini çıkarmak için a'ya dönüştürün . Ancak, yerel işlevlere sahip olduğunu gördüm, bu yüzden onları bir fonkta tutamayabilirsiniz. Sadece bir yönteme derlenebilir int h(string[] s) { }.
TheLethalCoder

Burada basitleştirilebilecek çok daha fazlası olduğuna eminim ...
TheLethalCoder

@TheLethalCoder Bunu farklı bir forma dönüştürmüyorum, cevapları fonksiyon olarak sevmiyorum (dediğin gibi lambda olmanıza gerek yok). Evet ... her şey şişirilmiş hissediyor ... ama mutasyon geçirerek iyi geçirdim ve önemli bir ilerleme kaydetmedim, bu yüzden birkaç geçiş 'golf' yaptım ve ittim. Daha kısa bir sürüm göndermekten çekinmeyin, buna daha az bağlıyım.
VisualMelon

Sadece bir yönteme dönüştürmek ve tüm konsol malzemelerini kaldırmak, artık gerekli olmayacak şekilde, 50-100 bayt kapatacak (sadece bir tahmin, ancak çok fazla kapatacak).
TheLethalCoder

@TheLethalCoder gerçekten; Fonksiyonları cevap olarak göndermekten hoşlanmıyorum. Standart Giriş oldukça Standarttır ve 'tam bir program' her yerde derlenebilir ve çalıştırılabilir. Beni türetilmemiş lambdalar üzerinde başlatmayın ... Açıkçası, rakip bir Java yanıtı olsaydı, o zaman standartlarımızı biraz gevşetmek zorunda kalırdım ...
VisualMelon

1

Salyangoz , 48 bayt

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

Ungolfed:

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

0

JavaScript (ES6), 192 bayt

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>

Başarısız Kaleleri Algılama'ya verdiğim cevaba dayanarak . İlk olarak dize, şeklin etrafında bir alan oluşturmak için boşluklarla doldurulur. Daha sonra RegExp, biri bitişik, biri @boşluk içeren iki bitişik kare arar ve ikisini de bir ile değiştirir @. Bunu yapamazsa, bir alanı bir@ yeni bir delik olarak sayar. Son olarak, çevredeki alanı hesaba katarak çıkarılır.


Bir çeşit TIO bağlantısı sağlayabilir misiniz? Teşekkürler!
HyperNeutrino
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.