Скільки отворів?


17

Виклик

Давши графічний вклад фігури, визначте, скільки дірок у ній є.

Не дублювати

Це питання було позначене як можливий дублікат графських островів . Я вважаю, що цей виклик відрізняється від виклику графського острова, оскільки в цьому ви повинні вирішити, як усунути блоки, які стосуються кордону.

Вхідні дані

Вхід буде наданий у вигляді двовимірної форми введення, або рядок багаторядкових, масив рядків або масив символьних масивів. Це являє собою форму. Форма гарантується лише з одного шматка, з'єднаного краєм. Вкажіть, будь ласка, як ви бажаєте приймати дані

Вихідні дані

Вихід - це одне ціле число, вказуючи, скільки отворів у формі. Дозвільна лінія нового рядка дозволена, але жодна інша провідна або кінцева пробіли. Іншими словами, висновок повинен відповідати регулярному виразу ^\d+\n?$.

Що таке дірка?

Це поодинокі отвори:

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

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

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

Це не дірки:

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

###
#  
###

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

Досить багато, якщо це зазор приєднується до зовнішнього краю, це не отвір.

Тестові справи

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

Ви можете використовувати будь-який символ замість "#" та замість пробілів.

Критерії об'єктивного оцінювання

Оцінка задається як кількість байтів у вашій програмі.

Перемога

Переможцем стане подання з найнижчою оцінкою до 4 квітня.



2
Чи можете ви додати ###|# #|## тестовий випадок? Це повинно бути 0, правда?
Мартін Ендер


1
Можливий дублікат Code-Golf: Count Count Islands
Matthew Roh

@SIGSEGV Дякую, що вказали на це; однак я вважаю, що цей виклик має критичну складову, що робить його досить відмінним від іншого виклику, щоб гарантувати власну посаду (я відредагував різницю). Будь ласка, дайте мені знати, що ви думаєте, і ми, можливо, захочемо розпочати дискусію в чаті, якщо це необхідно.
HyperNeutrino

Відповіді:


12

MATLAB / Октава, 18 байт

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

Спробуйте в Інтернеті!

Це анонімна функція, яка приймає логічну матрицю як вхідний. Об'єкт формується trueзаписами (із заданим підключенням), порожній простір - це falseзаписи.

bweuler потім обчислює номер ейлера двійкового зображення, представленого цією матрицею, тобто кількість об'єктів мінус кількість отворів.


8

Математика, 59 57 байт

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

Для цього є вбудований. Вводить вхід у вигляді 2D матриці 1s (стіни) та 0s (отвори). Для зручності тут наведено всі тестові приклади у такому форматі введення:

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

Альтернативне рішення, 59 байт

Це був мій оригінальний підхід. Він також базується на вбудованих компонентах, але не враховує отвори безпосередньо (натомість він розглядає самі отвори як компоненти).

Max@*MorphologicalComponents@*DeleteBorderComponents@*Image

Приймає той же формат введення, що і вище, але з ролями 0s і 1s поміняється.

Причиною, що мені потрібно перетворити це на Imageперше, є те, що в іншому випадку Mathematica розглядає всі 1-елементи як частину одного компонента (тому що він розглядає матрицю як матрицю-мітка компонента). Отже, якщо будь-яка 1клітина межує з полем, вона видалила б їх усі. Якщо DeleteBorderComponentsзамість цього використовується зображення, воно зробить неявну перевірку підключення, щоб знайти компоненти.

Крім того, я міг би зателефонувати MorphologicalComponents спочатку , що перетворило б вхід у відповідну матрицю міток, але якщо я DeleteBorderComponentsповторю, це вже не гарантує, що максимальна мітка компонента відповідає кількості компонентів (тому що я можу видалити менший компонент).


5
Дійсно, Mathematica має вбудовані для всього всього ...
Містер Xcoder

3
@ Mr.Xcoder У мене є гарна ідея виклику: знайдіть завдання, для якого у Mathematica немає вбудованого.
HyperNeutrino

@HyperNeutrino хороша ідея, але я думаю, що користувачі Mathematica будуть сильно знижувати це, на жаль, і я не знаю, чи спільнота відреагує добре ... =]
Містер Xcoder

1
@HyperNeutrino, мабуть, для цього теж є вбудований :-)
Брайан Мінтон

@BrianMinton Haha. Напевно, в Mathematica є вбудований модуль GenerateBuiltin. Він створює вбудований для будь-якого виклику, який не має вбудованого. Також мені погано поступає в папку "Вхідні" Мартіна Ендера, тому якщо ви хочете, продовжимо цю дискусію тут
HyperNeutrino

4

Perl 5 , 154 байти

152 байти коду + 2 байти для -p0прапора.

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

Спробуйте в Інтернеті!

Я думаю, що код досить зрозумілий.


Якщо вам потрібні деякі пояснення, щоб зрозуміти, ось декілька кроків перетворень, зроблених програмою на простому вході ( звідси ), з наступними поясненнями нижче:

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

######
# А
# ####
# #
#### #
######

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

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

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

По-перше, s/^ | $/A/gm;s/^.*\K | (?=.*$)/A/&&redoзамінять пробіли в межі (1-й регекс для лівого / правого, другий для нижнього / верхнього) на A(я вибираю цього символу досить довільно).
Потім отримуємо ширину форми /.*/;$@="@+"-1;.
Тепер ми хочемо замінити кожен простір, який з'єднаний з a, Aна A(тому що якщо простір підключено до а A, це означає, що він не може бути частиною отвору. Це зроблено for$%(A,X){(s/$%(.?.?.{$@})? /$%$1$%/s||s/ (.?.?.{$@})?$%/$%$1$%/s)&&redo}.) Ви помітите, що це робиться один раз для As і один для Xs - пояснення для Xнижче). Тут є два регулярні вирази: s/$%(.?.?.{$@})? /$%$1$%/sстосується пробілів, які знаходяться праворуч або внизу а A., і s/ (.?.?.{$@})?$%/$%$1$%/sз пробілами вгорі або зліва від а A.
На даний момент єдині пробіли, що залишилися в рядку, - це частина отворів.
Поки є ще пробіли, ми повторюємо:
- Щоб знати, скільки дірок, ми заміняємо пробіл на X( s/ /X/) і збільшуємо лічильник отворів ( $\++), і повторюємо всю програму (насправді ми хочемо лише повторити forцикл , але менше байтів, щоб повторити всю програму).
- Тоді forцикл замінить кожен простір, який з'єднаний Xз a X, а вони є частиною одного отвору.
Зрештою, $\|=0гарантує, що якщо немає отворів, 0замість порожнього рядка надрукується a . І $\друкується неявно завдяки -pпрапору.


4

Python 2, 282 байт

+100 для обробки діагональних дотиків TT_TT (чи справді це нам потрібно?)
-119 завдяки @Rod керівництву :)

Спробуйте в Інтернеті . Займає масив масивів символів "#" та пробілів як вхідні дані.

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

Шукайте перший пробіл і позначте його як не порожній ('#'). Рекурсивно перевіряйте все, що його оточує, заповнюючи всі порожні комірки. Якщо будь-яка порожня комірка поточного "отвору" виявиться на прикордонному лічильнику, не зміниться, інакше вона буде збільшена на 1. Повторіть процес, поки не буде більше пробілів.


1
Ви можете використовувати sum(A,[])для вирівнювання
стрижень

1
Також ви можете перевірити цю відповідь , вона має таку ж рекурсивну логіку, а також маєте деякі інші хитрощі (наприклад, функція "перейменування" у першому рядку)
Rod

@Rod Хитрість із сумою дуже гарна, дякую. Зараз я працюю над тим, щоб отримати всі 8 напрямків без усієї цієї потворності, ваша відповідь може допомогти. Я поновлю після цього
Dead Possum

Зауважте, що у своїй відповіді я викликав рекурсивну функцію всередині списку, щоб використовувати менше байтів, але у вашому випадку ви можете перевірити довжину списку, щоб побачити, чи належить поточна комірка до меж (вміст списку буде багато Nones, але це не має значення)
Rod

1
Ви можете використовувати розпакування списку на x=T[0];y=T[1]-> x,y=T, вам (напевно) не потрібно декларувати g=1в 3-му рядку, і ви можете використовувати <або >порівнювати рядки (це займеord() значення кожного напівкоксу) дозволяє замінити A[y][x]!='#'з A[y][x]<'#', так ' '<'#'.
Прут

3

Пітон 2, 233 225 222 байти

math_junkie : -8 байт

Бере 2d масив булів / цілих чисел (0/1) в якості вхідних даних

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

Спробуйте в Інтернеті!

Відформатована версія:

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
Ви можете зберегти кілька байтів, print sum(m(x,y)...а не a=іprint a
математика наркоман

1
Також деякі незначні гольфи з пробілів: TIO
математика наркоман

1

C # 7, 364 байти

Менш ніж задоволений цим ... можливо, хтось ще може розібратися з цим ... Якщо у мене буде енергія пізніше, я переверну першу петлю і побачу, чи це може допомогти обрізати межі перевірки.

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

Спробуйте в Інтернеті

Повна програма, приймає вхід до стандарту, вихід у стандартний вихід. Використовує роз'єднані набори для визначення попередніх отворів і коли вбиває будь-який дотик до меж (з деякою хитрістю для верхнього краю).

Форматований та коментований код:

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

Перетворіть його в a, Func<List<string>, int>щоб видалити пушки та консольні речі. Однак я бачив, що у вас є локальні функції, тому ви не можете мати їх у функції. Неможливо просто компілювати до методу int h(string[] s) { }.
TheLethalCoder

Я впевнений, що тут можна спростити набагато більше ...
TheLethalCoder

@TheLethalCoder Я не буду перетворювати це в іншу форму, мені не подобаються відповіді як функції (не потрібно було б лямбда, як ви сказали). Так ... вся справа відчуває себе роздутою ... але я добре пробував, мутуючи її, і не досягнув суттєвого прогресу, тому я зробив кілька пропусків "трохи" гольфу і штовхнув його. Не соромтеся подати більш коротку версію, я менш прив’язаний до цієї.
VisualMelon

Я маю на увазі, що просто перетворення його на метод і видалення всіх консольних речей, оскільки це більше не потрібно, збирається збивати 50-100 байт (лише здогадка, але це дуже збиває).
TheLethalCoder

@TheLethalCoder дійсно; Мені просто не подобається подавати функції як відповіді. Стандартне введення досить стандартне, а "повну програму" легко складати та запускати в будь-якому місці. Не запускайте мене на нетипізовані лямбда ... Очевидно, якби була конкуруюча відповідь на Java, то мені довелося б трохи
збивати

1

Равлики , 48 байт

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

Безумовно:

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

0

JavaScript (ES6), 192 байти

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>

На підставі моєї відповіді на виявлення невдалих замків . Спочатку рядок прокладений пробілами, щоб зробити область навколо форми. Потім RegExp шукає два сусідні квадрати, один, що містить an @, один, який містить пробіл, і замінює їх обидва на an @. Якщо він не може цього зробити, він заповнює пробіл із знаком "a" @і вважає це новою дірою. Нарешті, один віднімається для обліку навколишньої території.


Чи можете ви надати TIO-посилання якесь? Спасибі!
HyperNeutrino
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.