Combien de trous?

17

Défi

Étant donné l'entrée graphique d'une forme, déterminez le nombre de trous qu'il contient.

Pas de doublon

Cette question a été marquée comme un double possible des îles Count . Je crois que ce défi est différent du défi de Count Island parce que dans celui-ci, vous devez comprendre comment éliminer les blocs qui touchent la frontière.

Contribution

L'entrée sera donnée sous la forme d'une forme d'entrée 2D, soit une chaîne multiligne, un tableau de chaînes ou un tableau de tableaux de caractères. Cela représente la forme. La forme est garantie d'être en une seule pièce, reliée par le bord. Veuillez spécifier comment vous souhaitez que les données soient prises.

Production

La sortie est un entier unique indiquant le nombre de trous dans la forme. Une nouvelle ligne de fin est autorisée, mais aucun autre espace de début ou de fin. En d'autres termes, la sortie doit correspondre à l'expression régulière ^\d+\n?$.

Qu'est-ce qu'un trou?

Ce sont des trous simples:

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

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

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

Ce ne sont pas des trous:

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

###
#  
###

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

À peu près, si l'écart rejoint le bord extérieur, ce n'est pas un trou.

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

Vous pouvez utiliser n'importe quel caractère à la place du «#» et à la place des espaces.

Critères de notation des objectifs

Le score est donné comme le nombre d'octets dans votre programme.

Gagnant

Le gagnant sera la soumission avec le score le plus bas, avant le 4 avril.

HyperNeutrino
la source
1
En relation
VisualMelon
2
Pourriez-vous ajouter ###|# #|## comme cas de test? Cela devrait être 0, non?
Martin Ender
1
Connexes
Matthew Roh
1
Copie possible de Code-Golf: Count Islands
Matthew Roh
@SIGSEGV Merci de l'avoir signalé; cependant, je crois que ce défi a une composante critique qui le rend suffisamment différent de l'autre défi pour justifier son propre article (j'ai édité dans la différence). Faites-moi savoir ce que vous en pensez, et nous pourrons peut-être commencer une discussion dans le chat si nécessaire.
HyperNeutrino

Réponses:

12

MATLAB / Octave, 18 octets

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

Essayez-le en ligne!

Il s'agit d'une fonction anonyme prenant une matrice logique en entrée. L'objet est formé par les trueentrées (avec la connectivité spécifiée), l'espace vide sont les falseentrées.

bweuler calcule ensuite le nombre d'Euler de l'image binaire représentée par cette matrice, c'est-à-dire le nombre d'objets moins le nombre de trous.

flawr
la source
8

Mathematica, 59 57 octets

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

Il y a une fonction intégrée pour cela. Prend l'entrée comme une matrice 2D de 1s (murs) et 0s (trous). Pour plus de commodité, voici tous les cas de test dans ce format d'entrée:

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

Solution alternative, 59 octets

C'était mon approche originale. Il est également basé sur les composants intégrés liés aux composants, mais ne compte pas directement les trous (à la place, il traite les trous eux-mêmes comme des composants).

Max@*MorphologicalComponents@*DeleteBorderComponents@*Image

Prend le même format d'entrée que ci-dessus, mais avec les rôles de 0s et 1s échangés.

La raison pour laquelle je dois convertir ceci en Imagepremier est que, sinon, Mathematica considérerait toutes les 1cellules comme faisant partie d'un seul composant (car il traite la matrice comme une matrice d'étiquette de composant). Par conséquent, le cas échéant1 cellule borde la marge, elle les supprimerait toutes. Lorsque DeleteBorderComponentsest utilisé à la place sur une image, il effectuera une vérification de connectivité implicite pour trouver les composants.

Alternativement, je pourrais appeler d' MorphologicalComponents abord , ce qui transformerait l'entrée en une matrice d'étiquettes appropriée, mais si je le fais DeleteBorderComponentsensuite, il n'est plus garanti que l'étiquette de composant maximale correspond au nombre de composants (car je pourrais supprimer un composant plus petit).

Martin Ender
la source
5
Vraiment, Mathematica a des fonctions intégrées pour tout ...
M. Xcoder
3
@ Mr.Xcoder J'ai une bonne idée de défi: trouver un défi pour lequel Mathematica n'a pas de fonction intégrée.
HyperNeutrino
@HyperNeutrino bonne idée, mais je pense que les utilisateurs de Mathematica vont fortement le déprécier, malheureusement, et je ne sais pas si la communauté réagira bien ... =]
M. Xcoder
1
@HyperNeutrino, il y a probablement aussi une fonction intégrée pour cela :-)
Brian Minton
@BrianMinton Haha. Il y a probablement un Mathematica intégré appelé GenerateBuiltin. Il génère un intégré pour tout défi qui n'a pas intégré. Aussi, je me sens mal pour la boîte de réception de Martin Ender, donc si vous voulez, continuons cette discussion ici
HyperNeutrino
4

Perl 5 , 154 octets

152 octets de code + 2 octets pour l' -p0indicateur.

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

Essayez-le en ligne!

Je pense que le code est assez explicite.


Si vous avez besoin d'explications pour comprendre, voici quelques étapes des transformations effectuées par le programme sur une simple entrée (provenant de ici ), suivies de quelques explications ci-dessous:

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

######
# UNE
# ####
# # #
#### #
######

######
#AAAAA
#UNE####
#UNE# #
#### #
######

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

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

Tout d'abord, s/^ | $/A/gm;s/^.*\K | (?=.*$)/A/&&redoremplacera les espaces dans la bordure (1er regex pour gauche / droite, 2e pour bas / haut) par A(je choisis ce caractère assez arbitraire).
Ensuite, nous obtenons la largeur de la forme avec /.*/;$@="@+"-1;.
Maintenant, nous voulons remplacer chaque espace connecté à un Apar un A(car si un espace est connecté à un A, cela signifie qu'il ne peut pas faire partie d'un trou. Cela se fait par for$%(A,X){(s/$%(.?.?.{$@})? /$%$1$%/s||s/ (.?.?.{$@})?$%/$%$1$%/s)&&redo}. (Vous remarquerez que cela se fait une fois pour le As et un pour les Xs - les explications pour le Xsont ci-dessous). Il y a deux expressions rationnelles ici: s/$%(.?.?.{$@})? /$%$1$%/straite les espaces qui sont à droite ou en bas de a A. Et s/ (.?.?.{$@})?$%/$%$1$%/savec les espaces en haut ou à gauche de a A.
À ce stade, les seuls espaces qui restent dans la chaîne font partie des trous.
Tant qu'il y a encore des espaces, nous répétons:
- Pour savoir combien il y a de trous, nous remplaçons un espace par un X( s/ /X/) et incrémentons le compteur des trous ( $\++), et refaire tout le programme (en fait, nous voulons seulement refaire la forboucle , mais c'est moins d'octets pour refaire tout le programme).
- Ensuite, la forboucle remplacera chaque espace connecté à un Xpar un X, car ils font partie du même trou.
À la fin, $\|=0garantit que s'il n'y a pas de trous, un 0est imprimé au lieu d'une chaîne vide. Et $\est implicitement imprimé grâce au -pdrapeau.

Dada
la source
4

Python 2, 282 octets

+100 pour gérer les touches diagonales TT_TT (en avons-nous vraiment besoin?)
-119 grâce au guidage @Rod :)

Essayez-le en ligne . Prend un tableau de tableaux de caractères «#» et d'espaces en entrée.

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

Recherche le premier espace blanc et le marque comme non vide ('#'). Vérifiez récursivement tout ce qui l'entoure, tout en remplissant toutes les cellules vides. Si une cellule vide du "trou" actuel semble être sur le compteur de bordure, elle ne changera pas, sinon elle serait augmentée de 1. Répétez le processus jusqu'à ce qu'il n'y ait plus d'espaces blancs.

Possum mort
la source
1
Vous pouvez utiliser sum(A,[])pour aplatir
Rod
1
Vous pouvez également vérifier cette réponse , elle a la même logique récursive, et a également d'autres astuces (comme la fonction "renommer" dans la première ligne)
Rod
@Rod Trick avec sum est très bon, merci. Je travaille maintenant à obtenir les 8 directions sans toute cette laideur, votre réponse pourrait vous aider. Je mettrai à jour après cela
Dead Possum
Notez que dans ma réponse, j'ai appelé la fonction récursive dans une compréhension de liste juste pour utiliser moins d'octets, mais dans votre cas, vous pouvez vérifier la longueur de la liste pour voir si la cellule actuelle appartient à des bordures (le contenu de la liste sera beaucoup de Nones, mais ce n'est pas pertinent)
Rod
1
Vous pouvez utiliser le déballage de liste sur x=T[0];y=T[1]-> x,y=T, vous n'avez (probablement) pas besoin de déclarer g=1sur la 3ème ligne, et vous pouvez utiliser <ou >comparer des chaînes (cela prendra la ord()valeur de chaque caractère) vous permettant de remplacer A[y][x]!='#'par A[y][x]<'#', depuis ' '<'#'.
Rod
3

Python 2, 233 225 222 octets

math_junkie : -8 octets

Prend un tableau 2D de booléens / entiers (0/1) en entrée

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

Essayez-le en ligne!

Version formatée:

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))
pan Pan
la source
1
Vous pouvez enregistrer quelques octets avec print sum(m(x,y)...au lieu de a=etprint a
junkie math
1
En outre, quelques golfs mineurs en espace blanc: TIO
junkie de mathématiques
1

C # 7, 364 octets

Moins que satisfait de cela ... peut-être que quelqu'un d'autre peut le régler ... Si j'ai l'énergie plus tard, j'inverserai la première boucle, et voir si cela peut aider à couper les limites.

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

Essayez-le en ligne

Programme complet, accepte l'entrée vers l'entrée standard, la sortie vers la sortie standard. Utilise des ensembles disjoints pour déterminer les trous provisoires et quand tue, touchez les bordures (avec un peu de dodgyness pour le bord supérieur).

Code formaté et commenté:

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);
    }
}
VisualMelon
la source
Convertissez-le en a Func<List<string>, int>pour supprimer les trucs fluff et console. Cependant, j'ai vu que vous avez des fonctions locales, donc vous ne pourrez peut-être pas les avoir dans une fonction. Pourrait simplement compiler en une méthode int h(string[] s) { }.
TheLethalCoder
Je suis sûr qu'il y a beaucoup plus à simplifier ici ...
TheLethalCoder
@TheLethalCoder Je ne convertirai pas ceci sous une forme différente, je n'aime pas les réponses en tant que fonctions (je n'aurais pas besoin d'être un lambda, comme vous l'avez dit). Ouais ... le tout semble gonflé ... mais j'ai passé un bon moment à le muter et je n'ai pas fait de progrès substantiel, alors j'ai fait quelques passes de golf "minuscule" et je l'ai poussé. N'hésitez pas à soumettre une version plus courte, je suis moins attaché à celle-ci.
VisualMelon
Je veux dire simplement le convertir en une méthode et supprimer tous les trucs de la console, car cela ne serait plus nécessaire, va faire tomber 50 à 100 octets (juste une supposition mais ça va beaucoup).
TheLethalCoder
@TheLethalCoder en effet; Je n'aime tout simplement pas soumettre des fonctions comme réponses. L'entrée standard est assez standard, et un «programme complet» est facile à compiler et à exécuter n'importe où. Ne me lancez pas sur les lambdas non typés ... Évidemment, s'il y avait une réponse Java concurrente, alors je devrais
relâcher
1

Escargots , 48 octets

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

Non golfé:

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

JavaScript (ES6), 192 octets

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>

Basé sur ma réponse à Détecter les châteaux défaillants . La chaîne est d'abord rembourrée avec des espaces pour créer une zone autour de la forme. Le RegExp recherche ensuite deux carrés adjacents, l'un contenant un @, l'autre contenant un espace, et les remplace tous les deux par un@ . S'il ne peut pas faire cela, il remplit un espace avec un @et le compte comme un nouveau trou. Enfin, un est soustrait pour tenir compte de la zone environnante.

Neil
la source
Pouvez-vous fournir un lien TIO quelconque? Merci!
HyperNeutrino