Точки вирізу в лабіринті


13

Лабіринт подається у вигляді матриці 0s (стіни) та 1s (прохідний простір) у будь-якому зручному форматі. Кожна клітина вважається пов'язаною зі своїми 4 (або меншими) ортогональними сусідами. Підключений компонент являє собою набір прохідних клітин все транзитивно з'єднаних один з одним. Ваше завдання - визначити точки вирізу - гойдалки, які, якщо їх перетворити на стіни, змінили б кількість з'єднаних компонентів. Виведіть булеву матрицю з 1-х лише в цих місцях. Мета - зробити це в найменших байтах коду.

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

in:
11101001
11011101
00000001
11101111
11110101
00011111
10110001
11111111
out:
01000000
00001001
00000001
00000101
00110000
00010000
00000000
11100000

in:
1111111111111111
1000000000000001
1111111111111101
0000000000000101
1111111111110101
1000000000010101
1011111111010101
1010000001010101
1010111101010101
1010101111010101
1010100000010101
1010111111110101
1010000000000101
1011111111111101
1000000000000001
1111111111111111
out:
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000

in:
1011010001111010
1111111011101101
1110010101001011
1111001110010010
1111010000101001
0111101001000101
0011100111110010
1001110011111110
0101000011100011
1110110101001110
0010100111000110
1000110111011010
0100101000100101
0001010101100011
1001010000111101
1000111011000010
out:
0000000000111010
1011110001001000
0000000000000011
0000000100010000
0000010000101000
0000001000000100
0000000011000000
1001100000011110
0000000001000010
0110100001000110
0000100101000010
1000100000000000
0100001000000100
0000000100100001
0000010000111000
0000010000000010

тож знайдіть усі мости у всіх підграфах
HyperNeutrino

1
Я думаю, що виклик виграє від покрокового прикладу для меншої матриці.
Містер Xcoder

1
@HyperNeutrino міст - це щось інше - це край (не вершина), видалення якого збільшує кількість підключених компонентів
ngn

1
@HyperNeutrino також, підграф не зовсім такий, як підключений компонент
ngn

1
@Notatree Ти маєш рацію. Я зробив помилку. Зараз це вже пізно виправити, але я сподіваюся, що це не зіпсує задоволення.
ngn

Відповіді:


3

Стакс , 40 байт

Çóê↓â.Φ}╞│*w<(♦◙¼ñ£º█¢,D`ì♥W4·☺╛gÇÜ♠╗4D┬

Запуск та налагодження тестових випадків

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

{2%{_xi48&GxG=-}_?m}{'1'2|e{"12|21".22RjMJguHgu%

Основна операція підрахунку острова працює так.

  1. Замініть перший '1'на a '2'.
  2. Регекс замінити '12|21'на '22'.
  3. Розділіть пробіли.
  4. Транспонування матриці.
  5. Повторюйте з 2., поки рядок не повториться.
  6. Повторюйте з 1., поки '1'в рядку не буде більше . Кількість повторень - це кількість островів.

.

{               start map block over input string, composed of [ 01]
  2%            mod by 2. space and 0 yield 0. 1 yields 1. (a)
  {             start conditional block for the 1s.
    _           original char from string (b)
    xi48&       make copy of input with current character replaced with 0
    G           jump to unbalanced }, then return; counts islands (c)
    xG          counts islands in original input (d)
    =           are (c) and (d) equal? 0 or 1 (e)
    -           b - e; this is 1 iff this character is a bridge
  }             end conditional block
  _?            execute block if (a) is 1, otherwise use original char from string
m               close block and perform map over input
}               goto target - count islands and return
{               start generator block
  '1'2|e        replace the first 1 with a 2
  {             start generator block
    "12|21".22R replace "12" and "21" with "22"
    jMJ         split into rows, transpose, and rejoin with spaces
  gu            generate values until any duplicate is encountered
  H             keep the last value
gu              generate values until any duplicate is encountered
%               count number of iterations it took

Бонусна програма 44 байтів - ця версія вводить і виводить у форматі сітки.


чи обробляє другий приклад протягом хвилини на вашому комп’ютері?
ngn

@ngn: на цьому ноутбуці середнього класу в Chrome це три приклади в 41-х роках. Також я просто зафіксував основне посилання. Я випадково залишив його встановленим на старішу непрацюючу версію.
рекурсивна

3

MATL , 26 байт

n:"GG0@(,w4&1ZIuz]=~]vGZye

Вхід є числовою матрицею, яка використовується ;як роздільник рядків.

Спробуйте в Інтернеті! Або перевірити всі тестові випадки .

Пояснення

n           % Implicit input: matrix. Push number of elements, N
:           % Range: gives [1 2 ... N]
"           % For each k in [1 2 ... N]
  GG        %   Push input matrix twice
  0@(       %   Write 0 at position k (in column-major order: down, then across).
            %   The stack now contains the original matrix and a modified matrix
            %   with 0 at position k
  ,         %   Do twice
    w       %     Swap
    4       %     Push 4. This specifies 4-element neighbourhood
    &1ZI    %     Label each connected component, using the specified
            %     neighbourhood. This replaces each 1 in the matrix by a
            %     positive integer according to the connected component it
            %     belongs to
    u       %     Unique: gives a vector of deduplicate elements
    z       %     Number of nonzeros. This is the number of connected components
  ]         %   End
  =~        %   Are they different? Gives true of false
]           % End
v           % Concatenate stack into a column vector
GZye        % Reshape (in column-major order) according to size of input matrix.
            % Implicit display

2

Perl 5 , -p0 105 101 96 93 90 89 байт

Використовується bзамість 1введення.

Переконайтесь, що матриця на STDIN закінчується новим рядком

#!/usr/bin/perl -p0
s%b%$_="$`z$'";s:|.:/
/>s#(\pL)(.{@{-}}|)(?!\1)(\pL)#$&|a.$2.a#se&&y/{c/z />0:seg&/\B/%eg

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

Використовує 3 рівні заміщення!

Ця 87-байтна версія і в форматі вводу, і у виході простіша для інтерпретації, але не конкурує, оскільки у виводі використовується 3 різних символи:

#!/usr/bin/perl -0p
s%b%$_="$`z$'";s:|.:/
/>s#(\w)(.{@{-}}|)(?!\1)(\w)#$&|a.$2.a#se&&y/{c/z />0:seg&/\B/%eg

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

Зберегти інший байт ( sмодифікатор регулярного виразів) в обох версіях легко , використовуючи в якості термінатора рядків (замість нового рядка) деякі різні (не буквено-цифрові) символи, але це робить введення знову нечитабельним.

Як це працює

Розглянемо підміну

s#(\w)(.{columns}|)(?!1)(\w)#c$2c#s

Тут ви знайдете дві букви, які є різними та розташовані поруч один з одним по горизонталі чи вертикалі, і замініть їх на c. У лабіринті, шлях якого повністю складається з літери, bнічого не відбудеться, оскільки букви однакові, але як тільки одна з букв буде замінена на іншу (наприклад z), цей лист і сусід будуть замінені на cповторне застосування заливка з’єднаного компонента з cнасінням z.

Однак у цьому випадку я не хочу повного заповнення. Я хочу заповнити лише одну із сусідніх озброєнь z, тому після першого кроку я хочу zпіти. Це вже працює з c$2cзаміною, але пізніше я хочу перезапустити заливку уздовж іншої руки, починаючи з тієї ж точки, і я не знаю, хто з cs спочатку zвже не був. Тому замість цього я використовую

s#(\w)(.{columns}|)(?!\1)(\w)#$&|a.$2.a#se

b | aє c, b | cє cі z | aє {. Тож у лабіринті зі складеними доріжками bта насінням zна першому кроці bзаміняться cі zзаміняться {котрими не буквою і не збігаються \wі тому не спричинять подальших заповнень. cОднак буде продовжувати подальший Flood-заливку збирається і один сусід руки насіння заповнюється. Наприклад, починаючи з

  b                      c
  b                      c
bbzbb       becomes    bb{bb
  b                      b
  b                      b

Тоді я можу замінити всі c деякими літерами (наприклад -) і замінити {ще zраз, щоб перезапустити заливку:

  -                      -
  -                      -
bbzbb       becomes    cc{bb
  b                      b
  b                      b

і повторюйте цей процес, поки не перетворяться всі сусіди насіння. Якби я тоді ще раз замінити {на zі повені заповнення:

  -                      -
  -                      -
--z--       stays      --z--
  -                      -
  -                      -

В zзалишається за в кінці , тому що немає сусідів , щоб зробити перетворення с. Це дає зрозуміти, що відбувається в наступному фрагменті коду:

/\n/ >                                    

Знайдіть перший рядок. Початкове зміщення зараз у@-

s#(\w)(.{@{-}}|)(?!\1)(\w)#$&|a.$2.a#se

Регекс, обговорений вище, @{-}як кількість стовпців (оскільки звичайна @-плутає парсер-аналізатор і не належним чином замінює)

&&

/\n/Завжди добре ведеться і заміна вірна до тих пір , як ми можемо все ще повінь заповнення. Тож частина after &&виконується, якщо виконана заливка однієї руки. Якщо ні, ліва сторона оцінює порожній рядок

y/{c/z / > 0

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

s:|.: code :seg

Отже, якщо це виконується на початковій рядку $_з zпозицією насіннєвого елемента, фрагмент коду всередині буде виконуватися багато разів, здебільшого не повертаючи нічого, окрім 1кожного разу, коли сусідня рука заповнюється затопленою. Ефективно $_знищується та замінюється на стільки 1, на скільки приєднані підключені компоненти z. Зауважте, що цикл потрібно виконати до суми розмірів компонентів + ​​кількості разів на озброєння, але це нормально, оскільки це "кількість символів, включаючи нові рядки * 2 + 1" разів.

Лабіринт відключається, якщо немає 1(порожня рядок, ізольована вершина) або якщо є більше 1 руки (більше 2 1с). Це можна перевірити, використовуючи регулярний вираз /\B/(це дає 0замість 1старих версій perl. Сперечається, який з них неправильний). На жаль, якщо це не відповідає, це дасть порожній рядок замість 0. Однак s:|.: code :segбув розроблений , щоб завжди повертати непарне число так, виконавши &з /\B/це дасть 0або 1.

Залишилося лише пройти весь вхідний масив і в кожній прохідній позиції посіяти з zпідрахунком і підключити руки. Це легко зробити за допомогою:

s%b%$_="$`z$'"; code %eg

Єдина проблема полягає в тому, що в положеннях, які не мають змоги зберігати, зберігається старе значення. Оскільки нам потрібен 0s, це означає, що оригінальний вхідний масив повинен 0знаходитись у положеннях, що не готуються, та 0збігаються \wв оригінальній підстановці і спричинить заповнення. Тому я використовую \pLнатомість (лише відповідні букви).


2

Java 8, 503 489 459 455 байт

int R,C,v[][];m->{int c[][]=new int[R=m.length][C=m[0].length],r[][]=new int[R][C],i=R*C,t,u;for(;i-->0;)c[t=i/C][u=i%C]=m[t][u];for(;++i<R*C;r[t][u]=i(c)!=i(m)?1:0,c[t][u]=m[t][u])c[t=i/C][u=i%C]=0;return r;}int i(int[][]m){int r=0,i=0,t,u;for(v=new int[R][C];i<R*C;)if(m[t=i/C][u=i++%C]>v[t][u]){d(m,t,u);r++;}return r;}void d(int[][]m,int r,int c){v[r][c]=1;for(int k=-3,t,u;k<4;k+=2)if((t=r+k/2)>=0&t<R&(u=c+k%2-k/2)>=0&u<C&&m[t][u]>v[t][u])d(m,t,u);}

-18 байт завдяки @ceilingcat .

Пояснення:

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

int R,C,                    // Amount of rows/columns on class-level
    v[][];                  // Visited-matrix on class-level

m->{                        // Method with int-matrix as both parameter and return-type
  int c[][]=new int[R=m.length][C=m[0].length],
                            //  Create a copy-matrix, and set `R` and `C`
      r[][]=new int[R][C],  //  Create the result-matrix
      i=R*C,                //  Index-integer
      t,u;                  //  Temp integers
  for(;i-->0;)              //  Loop `i` over each cell:
    c[t=i/C][u=i%C]=m[t][u];//   And copy the values of the input to the copy-matrix
  for(;++i<R*C              //  Loop over the cells again:
      ;                     //    After every iteration:
       r[t][u]=i(c)!=i(m)?  //     If the amount of islands in `c` and `m` are different
        1                   //      Set the current cell in the result-matrix to 1
       :                    //     Else:
        0,                  //      Set it to 0
       c[t][u]=m[t][u])     //     And set the copy-value back again
    c[t=i/C][u=i%C]=0;      //   Change the current value in the copy-matrix to 0
  return r;}                //  Return the result-matrix

// Separated method to determine the amount of islands in a matrix
int i(int[][]m){
  int r=0,                  //  Result-count, starting at 0
      i=0,                  //  Index integer
      t,u;                  //  Temp integers
  for(v=new int[R][C];      //  Reset the visited array
      i<R*C;)               //  Loop over the cells
    if(m[t=i/C][t=i++%C]    //   If the current cell is a 1,
       >v[t][u]){           //   and we haven't visited it yet:
      d(m,i,j);             //    Check every direction around this cell
      r++;}                 //    And raise the result-counter by 1
   return r;}               //  Return the result-counter

// Separated method to check each direction around a cell
void d(int[][]m,int r,int c){
  v[r][c]=1;                //  Flag this cell as visited
  for(int k=-3,u,t;k<4;k+=2)//  Loop over the four directions:
    if((t=r+k/2)>=0&t<R&(u=c+k%2-k/2)>=0&u<C
                            //   If the cell in the direction is within bounds,
       &&m[t][u]            //   and it's a path we can walk,
         >v[t][u])          //   and we haven't visited it yet:
      d(m,i,j);}            //    Do a recursive call for this cell

1

Python 2 , 290 байт

lambda m:[[b([[C and(I,J)!=(i,j)for J,C in e(R)]for I,R in e(m)])!=b(eval(`m`))for j,c in e(r)]for i,r in e(m)]
def F(m,i,j):
	if len(m)>i>=0<=j<len(m[i])>0<m[i][j]:m[i][j]=0;F(m,i,j+1);F(m,i,j-1);F(m,i+1,j);F(m,i-1,j)
b=lambda m:sum(F(m,i,j)or c for i,r in e(m)for j,c in e(r))
e=enumerate

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

-11 байт завдяки Rod
-11 байт завдяки Лінн


1
Коротше використовувати F(m,i,j)для кожного елемента, заощаджуючи 11 байт
Rod

for q in((i,j+1),(i,j-1),(i+1,j),(i-1,j)):-> for q in(i,j+1),(i,j-1),(i+1,j),(i-1,j):- rm
external parens

Оскільки Fнеявно повертається None, ви можете використовувати F(m,i,j)or cзамість [F(m,i,j)]and c.
Лінн

Також and m[i][j]може бути >0<m[i][j]і [q[:]for q in m]може бути eval(`m`).
Лінн

@Lynn ти маєш на увазі eval ('m')? чи не поверне це той самий екземпляр списку?
ngn


1

Javascript 122 байти

Введення / виведення у вигляді рядкової рядок.

m=>m.replace(/./g,(v,p,m,n=[...m],f=p=>n[p]==1&&(n[p]=0,v=f(p-1)+f(p+1)+f(p-w)+f(p+w)-1?1:0,1))=>(f(p),v),w=~m.search`\n`)

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

Менше гольфу

m=>{
  w = m.search('\n') + 1; // offset to the next row
  result = [...m].map( // for each cell
     ( v, // current value
       p  // current position
     ) => {
     n = [...m]; // work on a copy of the input
     // recursive fill function from position p
     // returns 1 if managed to fill at least 1 cell
     fill = (p) => {
        if (n[p] == 1)
        {
           n[p] = 0;
           // flag will be > 1 if the fill from the current point found disjointed areas
           // flag will be 0 if no area could be filled (isolated cell)
           var flag = fill(p+1) + fill(p-1) + fill(p+w) + fill(p-w);
           // v is modified repeatedly, during recursion
           // but I need the value at top level, when fill returns to original caller
           v = flag != 1 ? 1 : 0;
           return 1; // at least 1 cell filled
        }
        else
           return 0; // no fill
     }
     fill(p)
     return v // orginal value or modified by fill function
  }) 
}

Тест

var F=
m=>m.replace(/./g,(v,p,m,n=[...m],f=p=>n[p]==1&&(n[p]=0,v=f(p-1)+f(p+1)+f(p-w)+f(p+w)-1?1:0,1))=>(f(p),v),w=~m.search`\n`)

var test=`in:
11101001
11011101
00000001
11101111
11110101
00011111
10110001
11111111
out:
01000000
00001001
00000001
00000101
00110000
00010000
00000000
11100000

in:
1111111111111111
1000000000000001
1111111111111101
0000000000000101
1111111111110101
1000000000010101
1011111111010101
1010000001010101
1010111101010101
1010101111010101
1010100000010101
1010111111110101
1010000000000101
1011111111111101
1000000000000001
1111111111111111
out:
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000

in:
1011010001111010
1111111011101101
1110010101001011
1111001110010010
1111010000101001
0111101001000101
0011100111110010
1001110011111110
0101000011100011
1110110101001110
0010100111000110
1000110111011010
0100101000100101
0001010101100011
1001010000111101
1000111011000010
out:
0000000000111010
1011110001001000
0000000000000011
0000000100010000
0000010000101000
0000001000000100
0000000011000000
1001100000011110
0000000001000010
0110100001000110
0000100101000010
1000100000000000
0100001000000100
0000000100100001
0000010000111000
0000010000000010
`.match(/\d[10\n]+\d/g);
for(i = 0; test[2*i]; ++i)
{
   input = test[2*i]
   check = test[2*i+1]
   result = F(input)
   ok = check == result
   console.log('Test '+ i + ' ' + (ok?'OK':'FAIL'),
   '\n'+input, '\n'+result)
}

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