Quanti fori?


17

Sfida

Dato un input grafico di una forma, determina quanti buchi ci sono in essa.

Non duplicato

Questa domanda è stata contrassegnata come possibile duplicato di Count Islands . Credo che questa sfida sia diversa dalla sfida Count Island perché in questa devi capire come eliminare i blocchi che toccano il confine.

Ingresso

L'input verrà fornito come una forma 2D di input, una stringa multilinea, una matrice di stringhe o una matrice di array di caratteri. Questo rappresenta la forma. La forma è garantita in un solo pezzo, collegata da un bordo. Si prega di specificare come si desidera ricevere l'input.

Produzione

L'output è un singolo intero che indica quanti buchi ci sono nella forma. È consentita una nuova riga finale, ma nessun altro spazio bianco iniziale o finale. In altre parole, l'output deve corrispondere all'espressione regolare^\d+\n?$ .

Che cos'è un buco?

Questi sono fori singoli:

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

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

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

Questi non sono buchi:

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

###
#  
###

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

Praticamente, se lo spazio si unisce al bordo esterno, non è un buco.

Casi test

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

Puoi usare qualsiasi carattere al posto del '#' e al posto degli spazi.

Criteri di punteggio obiettivo

Il punteggio viene assegnato come numero di byte nel programma.

vincente

Il vincitore sarà l'invio con il punteggio più basso, entro il 4 aprile.



2
Potresti aggiungere ###|# #|## come test case? Dovrebbe essere 0vero?
Martin Ender,


1
Possibile duplicato di Code-Golf: Count Islands
Matthew Roh

@SIGSEGV Grazie per averlo sottolineato; tuttavia, credo che questa sfida abbia una componente critica che la rende abbastanza diversa dall'altra sfida da giustificare il proprio post (ho modificato la differenza). Per favore fatemi sapere cosa ne pensate e potremmo voler iniziare una discussione in chat, se necessario.
HyperNeutrino,

Risposte:


12

MATLAB / Ottava, 18 byte

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

Provalo online!

Questa è una funzione anonima che accetta una matrice logica come input. L'oggetto è formato dalle truevoci (con la connettività specificata), lo spazio vuoto sono le falsevoci.

bweuler quindi calcola il numero di eulero dell'immagine binaria rappresentata da quella matrice, ovvero il numero di oggetti meno il numero di fori.


8

Mathematica, 59 57 byte

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

C'è un built-in per questo. Accetta input come una matrice 2D di 1s (muri) e 0s (fori). Per comodità, ecco tutti i casi di test in questo formato di input:

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

Soluzione alternativa, 59 byte

Questo era il mio approccio originale. Si basa anche sui componenti integrati relativi ai componenti, ma non conta direttamente i fori (invece tratta i fori stessi come componenti).

Max@*MorphologicalComponents@*DeleteBorderComponents@*Image

Utilizza lo stesso formato di input di cui sopra, ma con i ruoli di 0s e 1s scambiati.

Il motivo per cui ho bisogno di convertire questo in un Imageprimo è che, altrimenti, Mathematica considererebbe tutte le 1celle come parte di un singolo componente (perché tratta la matrice come una matrice di etichetta componente). Quindi, se una qualsiasi 1cella delimita il margine, eliminerebbe tutti. quandoDeleteBorderComponents viene invece utilizzato su un'immagine, eseguirà un controllo di connettività implicito per trovare i componenti.

In alternativa, potrei chiamare per MorphologicalComponents primo , il che trasformerebbe l'input in una matrice di etichette adatta, ma se lo faccioDeleteBorderComponents secondo luogo non è più garantito che l'etichetta massima del componente corrisponda al numero di componenti (perché potrei eliminare un componente più piccolo).


5
Davvero, Mathematica ha dei built-in per tutto ...
Mr. Xcoder,

3
@ Mr.Xcoder Ho una buona idea di sfida: trova una sfida per la quale Mathematica non è integrata.
HyperNeutrino,

@HyperNeutrino buona idea, ma penso che gli utenti di Mathematica lo voteranno pesantemente, sfortunatamente, e non so se la comunità reagirà bene ... =]
Mr. Xcoder

1
@HyperNeutrino, probabilmente anche per questo è incorporato :-)
Brian Minton

@BrianMinton Haha. Probabilmente c'è un built-in in Mathematica chiamato GenerateBuiltin. Genera un built-in per qualsiasi sfida che non ha un built-in. Inoltre, mi sento male per la posta in arrivo di Martin Ender, quindi se vuoi, continuiamo questa discussione qui
HyperNeutrino

4

Perl 5 , 154 byte

152 byte di codice + 2 byte per -p0flag.

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

Provalo online!

Penso che il codice sia abbastanza esplicativo.


Se hai bisogno di alcune spiegazioni per capire, ecco alcuni passaggi delle trasformazioni fatte dal programma su un semplice input (proveniente da qui ), seguito da alcune spiegazioni qui sotto:

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

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

######
#AAAAA
#UN####
# A # #
#### #
######

######
#AAAAA
#UN####
#ASCIA #
#### #
######

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

In primo luogo, s/^ | $/A/gm;s/^.*\K | (?=.*$)/A/&&redosostituirà gli spazi nel bordo (1a regex per sinistra / destra, 2a per fondo / alto) con A(scelgo quel personaggio abbastanza arbitrario).
Quindi, otteniamo la larghezza con la forma /.*/;$@="@+"-1;.
Ora, vogliamo sostituire ogni spazio collegato a un Acon un A(perché se uno spazio è collegato a un A, significa che non può far parte di un buco. È fatto da for$%(A,X){(s/$%(.?.?.{$@})? /$%$1$%/s||s/ (.?.?.{$@})?$%/$%$1$%/s)&&redo}. (Noterai che questo è fatto una volta per la As e una per le Xs - spiegazioni per la Xmuggito) Ci sono due regex qui: si s/$%(.?.?.{$@})? /$%$1$%/stratta degli spazi che sono a destra o in fondo a a A. E s/ (.?.?.{$@})?$%/$%$1$%/scon gli spazi in alto o a sinistra di a A.
A questo punto, gli unici spazi rimasti nella stringa fanno parte dei fori.
Mentre ci sono ancora spazi, ripetiamo:
- Per sapere quanti buchi ci sono, sostituiamo uno spazio con un X( s/ /X/) e incrementiamo il contatore dei buchi ( $\++), e ripetiamo l'intero programma (in realtà, vogliamo solo ripetere il forciclo , ma è meno byte per ripetere l'intero programma).
- Quindi, il forloop sostituirà ogni spazio collegato a Xcon a X, in quanto fanno parte dello stesso foro.
Alla fine, $\|=0assicura che se non ci sono buchi, 0viene stampato a anziché una stringa vuota. Ed $\è implicitamente stampato grazie alla -pbandiera.


4

Python 2, 282 byte

+100 per gestire i tocchi diagonali TT_TT (ne abbiamo davvero bisogno?)
-119 grazie alla guida @Rod :)

Provalo online . Accetta array di matrici di caratteri '#' e spazi bianchi come 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

Cerca il primo spazio bianco e contrassegnalo come non vuoto ('#'). Controlla ricorsivamente tutto ciò che lo circonda, riempiendo tutte le celle vuote. Se una cella vuota dell'attuale "buco" sembra trovarsi sul bordo del confine non cambierà, altrimenti aumenterebbe di 1. Ripetere il processo, fino a quando non ci sono più spazi bianchi.


1
Puoi usare sum(A,[])per appiattire
Rod

1
Inoltre puoi controllare questa risposta , ha la stessa logica ricorsiva e anche alcuni altri trucchi (come la funzione "rinomina" nella prima riga)
Rod

@Rod Il trucco con la somma è molto buono, grazie. Ora sto lavorando per ottenere tutte e 8 le direzioni senza tutta questa bruttezza, la tua risposta potrebbe aiutare. Dopo aggiornerò
Dead Possum,

Nota che sulla mia risposta ho chiamato la funzione ricorsiva all'interno di una comprensione dell'elenco solo per usare meno byte, ma nel tuo caso puoi controllare la lunghezza dell'elenco per vedere se la cella corrente appartiene ai bordi (il contenuto dell'elenco sarà molto Nones, ma questo è irrilevante)
Rod

1
È possibile utilizzare l'elenco disimballaggio su x=T[0];y=T[1]-> x,y=T, che (probabilmente) non c'è bisogno di dichiarare g=1sulla linea 3, ed è possibile utilizzare <o >per confrontare le stringhe (che prenderà il ord()valore di ogni char) che consente di sostituire A[y][x]!='#'con A[y][x]<'#', dal momento ' '<'#'.
Rod

3

Python 2, 233 225 222 byte

math_junkie : -8 byte

Prende 2d array di valori booleani / interi (0/1) come 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))

Provalo online!

Versione formattata:

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
Puoi salvare alcuni byte con print sum(m(x,y)...invece di a=eprint a
drogato di matematica il

1
Inoltre, alcuni piccoli campi da golf bianchi: TIO
drogato di matematica il

1

C # 7, 364 byte

Meno che soddisfatto di questo ... forse qualcun altro può risolverlo ... Se avrò l'energia in seguito, invertirò il primo ciclo e vedrò se ciò può aiutare a tagliare i limiti.

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

Provalo online

Programma completo, accetta input a standard in, output a standard out. Usa set disgiunti per determinare buchi provvisori e quando uccide qualsiasi tocco tocca i bordi (con un po 'di confusione per il bordo superiore).

Codice formattato e commentato:

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

Convertire in un Func<List<string>, int>per rimuovere la lanugine e roba console. Tuttavia, ho visto che hai funzioni locali, quindi potresti non essere in grado di averle in una funzione. Potrebbe semplicemente compilare un metodo int h(string[] s) { }.
TheLethalCoder

Sono sicuro che c'è molto di più che può essere semplificato qui ...
TheLethalCoder

@TheLethalCoder Non lo convertirò in una forma diversa, non mi piacciono le risposte come funzioni (non avrei bisogno di essere un lambda, come hai detto). Sì ... il tutto sembra gonfio ... ma ho passato un bel po 'di tempo a mutarlo e non ho fatto progressi sostanziali, quindi ho fatto alcuni passaggi di golf "aspro" e l'ho spinto. Sentiti libero di inviare una versione più breve, sono meno che attaccato a questo.
VisualMelon

Voglio dire, convertirlo in un metodo e rimuovere tutte le cose della console, dato che non sarebbe più necessario, eliminerà 50-100 byte (solo una supposizione ma andrà a finire molto).
TheLethalCoder

@TheLethalCoder infatti; Semplicemente non mi piace inviare le funzioni come risposte. Lo standard input è piuttosto standard e un "programma completo" è facile da compilare ed eseguire ovunque. Non farmi iniziare su lambda non tipizzati ... Ovviamente, se ci fosse una risposta Java in competizione, allora dovrei
allentare

1

Lumache , 48 byte

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

Ungolfed:

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

Sulla base della mia risposta a Rileva castelli falliti . Innanzitutto la stringa è riempita con spazi per creare un'area attorno alla forma. RegExp quindi cerca due quadrati adiacenti, uno contenente un @, uno contenente uno spazio e li sostituisce entrambi con un @. Se non può farlo, riempie uno spazio con un @e lo considera come un nuovo buco. Alla fine uno viene sottratto per tenere conto dell'area circostante.


Potete fornire un collegamento TIO di qualche tipo? Grazie!
HyperNeutrino,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.