Уникальные кирпичные плитки в прямоугольнике

13

Я просматривал Stackoverflow и увидел этот вопрос о мозаике прямоугольника MxN, и я подумал, что это будет здорово для игры в гольф. Вот задача.

Учитывая размерность M и N, напишите программу, которая выводит, сколько уникальных способов можно прямоугольнить MxN (N - количество строк, а не столбцов. Не то, чтобы это действительно имеет значение), учитывая эти ограничения.

  1. Все плитки 2х1 или 3х1
  2. Все плитки остаются в своем ряду (то есть все они горизонтальны)
  3. Между каждыми двумя смежными рядами плитки не должны быть выровнены, кроме как на двух концах
  4. M и N гарантированно будут по крайней мере 1

Например, допустимый лист 8x3 будет

  2    3     3
  |    |     |
  v    v     v
 _______________
|___|_____|_____| 
|_____|_____|___|
|___|_____|_____|

Но следующее будет недопустимым, потому что строки выравниваются

  2    3     3
  |    |     |
  v    v     v
 _______________
|___|_____|_____| 
|_____|___|_____|
|_____|_____|___|

Тестовые случаи:

8х3: 4

3х1: 1

1x1: 0

9x4: 10

Код гольф, поэтому самый короткий ответ выигрывает.

rtpax
источник
2
Ваше описание размера плиток, кажется, использует соглашение, отличное от размера прямоугольника. Плитка на самом деле 2x1или 3x1? Также есть выход для 4x1нуля?
FryAmTheEggman
1
Добро пожаловать. Хорошая концепция соревнований, однако, как правило, лучше всего использовать песочницу, чтобы выработать идеи соревнований, прежде чем отправлять их на главную.
Beefster
@FryAmTheEggman Похоже OP попытался сделать |ей не способствовать длине строки, используя представление , как это (где, если не трубка ( |), есть пробел).
Эрик Outgolfer
1
Упомянутый вопрос о SO больше не существует.
Арно

Ответы:

5

Желе , 20 байт

2*ḃ€2‘ÄṪ⁼¥Ƈ⁸ṗfƝẸ$€ċ0

Попробуйте онлайн!

Эрик Outgolfer
источник
Я знаю, что скорость не была частью спецификации, но она работала по тайм-ауту даже на 11x10 при работе на TIO. Мне было бы интересно объяснить, почему.
Ник Кеннеди
@NickKennedy Это слишком большой вклад. Для ширины 11 каждый ряд может иметь один из 9 различных элементов мозаичного изображения. Для ширины 11 и высоты 10 существует 9¹⁰ = 3486784401 возможных стен, включая недопустимые. Так работает декартова сила. Очевидно, у TIO нет времени, чтобы позволить моему решению вычислить весь массив стен (время ожидания истекает через 60 секунд). Я добавлю объяснение, когда у меня будет время.
Эрик Outgolfer
Благодарю. Я немного посмотрел на желе, но сейчас я полагаюсь на комментированные объяснения, чтобы понять, что делает код людей. Я предположил, учитывая проблему времени, что ваш грубый код заставляет решение, которое является разумным способом минимизации требований кода.
Ник Кеннеди
Из интереса я воссоздал в Jelly метод в моем коде R, используя первую часть вашего кода. Это на Попробуй онлайн! и хотя он значительно длиннее вашего, он обрабатывает большие числа. Обратите внимание, что в настоящее время он не обрабатывает 1 строку должным образом. Я подозреваю, что это может быть гораздо более кратким, но я новичок в желе.
Ник Кеннеди
4

JavaScript (ES6),  119 110 106 96  91 байт

Принимает вход как (N,M),

f=(n,m,p=0,g=(w,h=x=>g(p[g[w-=x]=1,w]||w)*g[w]--)=>w>3?h(2)+h(1):w>1&&f(n,m-1,g))=>m?g(n):1

Попробуйте онлайн!

комментарии

Примечание: этот код использует 3 разные функции, которые вызывают друг друга. Это немного затрудняет отслеживание области видимости переменных. Имейте в виду, чтограмм определяется в рамках е и час определяется в рамках грамм,

f = (                    // f is a recursive function taking:
  n,                     //   n = number of columns
  m,                     //   m = number of rows
  p = 0,                 //   p = object holding the previous row
  g = (                  //   g = recursive function taking:
    w,                   //     w = remaining width that needs to be filled in the
                         //         current row
    h = x =>             //     h = helper function taking x
                         // h body:
      g(                 //   recursive call to g:
        p[g[w -= x] = 1, //     subtract either 2 or 1 from w and mark this width as used
          w              //     test p[w]
        ]                //     pass p[w] if p[w] = 1 (which will force the next iteration
                         //     to fail immediately)
        || w             //     otherwise, pass w
      )                  //   end of recursive call
      * g[w]--           //   then restore g[w] to 0
  ) =>                   // g body:
    w > 3 ?              //   if w > 3, we need to insert at least 2 more bricks:
      h(2) + h(1)        //     invoke h with x = 2 and x = 1
    :                    //   else:
      w > 1              //     this is the last brick; we just check if it can be inserted
      &&                 //     abort if w is equal to 1 (a brick does not fit in there)
      f(                 //     otherwise, do a recursive call to f:
        n,               //       n is unchanged
        m - 1,           //       decrement m
        g                //       pass g as the new reference row
      )                  //     end of recursive call
) =>                     // f body:
  m ? g(n) : 1           //   yield 1 if we made it to the last row or call g otherwise
Arnauld
источник
1

R , 243 231 байт

function(m,n,i=2*0:(m%/%6)+m%%2,j=i+(m-3*i)/2,M=Map)`if`(m<2,0,sum((e=eigen(lengths(outer(p<-unlist(M(M,list(function(x,y)cumsum(2+1:y%in%x)),M(combn,j,i,s=F),j),F),p,Vectorize(intersect)))<2))$ve%*%diag(e$va^(n-1))%*%solve(e$ve)))

Попробуйте онлайн!

Версия с переносами строк:

function(m,n,i=2*0:(m%/%6)+m%%2,j=i+(m-3*i)/2,M=Map)`if`(m<2,0,
sum((e=eigen(lengths(outer(p<-unlist(M(M,list(function(x,y)cumsum(2+1:y%in%x)),
M(combn,j,i,s=F),j),F),p,Vectorize(intersect)))<2))$ve%*%diag(e$va^(n-1))%*%solve(e$ve)))

Обратите внимание, что нет рекурсии, и обрабатывает довольно большие значения m и n (например, 24x20 -> 3.3e19)

Вот закомментированный ответ, который работает более или менее так же, как и выше, но я удалил все функции, поэтому он действительно читабелен:

f <- function(m,n) {
  # First work out what potential combinations of 2s and 3s add up to m
  i <- 2*0:(m %/% 6) + m %% 2 # Vector with numbers of possible 3s
  j <- i + (m - 3 * i) / 2 # Vector with total number of 2s and 3s
  if (m < 2) {
    0 # If wall less than 2 wide, no point in continuing because answer is 0
  } else {
    # Work out all possible positions of threes for each set
    positions_of_threes <- Map(combn, j, i, simplify = FALSE)
    # Function to work out the cumulative distance along the wall for a given
    # Set of three positions and number of bricks
    make_cumulative_bricks <- function(pos_threes, n_bricks) {
      bricks <- 1:n_bricks %in% pos_threes
      cumsum(2 + bricks)
    }
    # Find all possible rows with cumulative width of wall
    # Note because this is a `Map` with depth two that needs to be vectorised
    # for both `positions_of_threes` and `j`, and we're using base R, the
    # function `make_cumulative_bricks` needs to be placed in a list
    cum_bricks <- Map(Map, list(make_cumulative_bricks), positions_of_threes, j)
    # Finally we have the list of possible rows of bricks as a flat list
    cum_bricks_unlisted <- unlist(cum_bricks, recursive = FALSE)
    # Vectorise the intersect function
    intersect_v <- Vectorize(intersect, SIMPLIFY = FALSE)
    # Find the length of all possible intersects between rows
    intersections <- outer(cum_bricks_unlisted, cum_bricks_unlisted, intersect_v)
    n_intersections <- lengths(intersections)
    # The ones not lined up will only have a single intersect at `m`
    not_lined_up <- n_intersections == 1
    # Now use method described at /programming//a/9459540/4998761
    # to calculate the (matrix of TRUE/FALSE for lined-up) to the power of `n`
    eigen_nlu <- eigen(not_lined_up)
    final_mat <- eigen_nlu$vectors %*%
      diag(eigen_nlu$values ^ (n - 1)) %*%
      solve(eigen_nlu$vectors)
    # The sum of this matrix is what we're looking for
    sum(final_mat)
  }
}
f(20,20)

Метод взятия матрицы и ее многократного умножения основан на вопросе о переполнении стека . Этот подход работает здесь, потому что он эффективно вычисляет совокупное количество ветвей через различные возможные ряды кирпичей.

Если внешние пакеты разрешены, я могу получить его до 192:

function(m,n,i=2*0:(m%/%6)+m%%2,j=i+(m-3*i)/2,M=purrr::map2)`if`(m<2,0,sum(expm::`%^%`(lengths(outer(p<-unlist(M(M(j,i,combn,s=F),j,M,~cumsum(2+1:.y%in%.)),F),p,Vectorize(intersect)))<2,n-1)))
Ник Кеннеди
источник
1

Желе , 26 байт

2*ḃ€2‘ÄṪ⁼¥Ƈ⁸œ&L¬ɗþ`æ*⁴’¤SS

Попробуйте онлайн!

Сломано:

Создайте список возможных стен в виде кумулятивных сумм с удаленным концом:

2*ḃ€2‘ÄṪ⁼¥Ƈ⁸

Найдите внешний стол всех возможных стен друг против друга, которые не имеют пересечений:

œ&L¬ɗþ`

Возьмите эту матрицу в степень (N-1) и затем суммируйте все это:

æ*⁴’¤SS

Использует первый бит из ответа @ EriktheOutgolfer, чтобы сгенерировать список возможных стен, а затем использует пересечение матриц и подход к возведению в матрицу из моего ответа R. Таким образом, он хорошо работает даже с большими буквами N. Это мой первый ответ на желе, и я подозреваю, что его можно играть в гольф больше. В идеале я бы хотел изменить первый раздел, чтобы время и требования к памяти не увеличивались экспоненциально с М.

Ник Кеннеди
источник
0

05AB1E , 42 байта

Åœʒ23yåP}€œ€`Ùε.¥¦¨}IиI.ÆÙεøyíø‚€€üQOO_P}O

Мне почти стыдно публиковать это, и определенно можно сыграть в ГЛОТЛ с другим подходом, но так как это заняло некоторое время, я решил опубликовать его в любом случае и сыграть в гольф отсюда. Задача выглядит проще, чем imo, но я определенно использую неправильный подход, и у меня есть ощущение, что 05AB1E может сделать около 25 байт ..

Попробуйте онлайн. ПРИМЕЧАНИЕ: он не только длинный, но и неэффективный, поскольку 9x4тестовый пример выполняется в течение 40 секунд на TIO.

Объяснение:

Ŝ             # Get all possible ways to sum to the (first) implicit input
               #  i.e. 8 → [[1,1,1,1,1,1,1,1],[1,1,1,1,1,1,2],[1,1,1,1,1,3],[1,1,1,1,2,2],[1,1,1,1,4],[1,1,1,2,3],[1,1,1,5],[1,1,2,2,2],[1,1,2,4],[1,1,3,3],[1,1,6],[1,2,2,3],[1,2,5],[1,3,4],[1,7],[2,2,2,2],[2,2,4],[2,3,3],[2,6],[3,5],[4,4],[8]]
  ʒ23yåP}      # Only leave those consisting of 2s and/or 3s
               #  → [[2,2,2,2],[2,3,3]]
         €œ    # For each: get all permutations
           €`  # Flatten this list of lists once
             Ù # And uniquify it (leaving all possible distinct rows of bricks)
               #  → [[2,2,2,2],[3,3,2],[3,2,3],[2,3,3]]
ε    }         # For each:
             #  Get the cumulative sum
   ¦¨          #  With the leading 0 and trailing first input removed
               #   → [[2,4,6],[3,6],[3,5],[2,5]]
      Iи       # Repeat this list the second input amount of times
               #  i.e. 3 → [[2,4,6],[3,6],[3,5],[2,5],[2,4,6],[3,6],[3,5],[2,5],[2,4,6],[3,6],[3,5],[2,5]]
        I    # Get all combinations of lists the size of the second input
           Ù   # And uniquify the result (leaving all possible distinct walls)
               #  → [[[2,4,6],[3,6],[3,5]],[[2,4,6],[3,6],[2,5]],[[2,4,6],[3,6],[2,4,6]],[[2,4,6],[3,6],[3,6]],[[2,4,6],[3,5],[2,5]],[[2,4,6],[3,5],[2,4,6]],[[2,4,6],[3,5],[3,6]],[[2,4,6],[3,5],[3,5]],[[2,4,6],[2,5],[2,4,6]],[[2,4,6],[2,5],[3,6]],[[2,4,6],[2,5],[3,5]],[[2,4,6],[2,5],[2,5]],[[2,4,6],[2,4,6],[3,6]],[[2,4,6],[2,4,6],[3,5]],[[2,4,6],[2,4,6],[2,5]],[[2,4,6],[2,4,6],[2,4,6]],[[3,6],[3,5],[2,5]],[[3,6],[3,5],[2,4,6]],[[3,6],[3,5],[3,6]],[[3,6],[3,5],[3,5]],[[3,6],[2,5],[2,4,6]],[[3,6],[2,5],[3,6]],[[3,6],[2,5],[3,5]],[[3,6],[2,5],[2,5]],[[3,6],[2,4,6],[3,6]],[[3,6],[2,4,6],[3,5]],[[3,6],[2,4,6],[2,5]],[[3,6],[2,4,6],[2,4,6]],[[3,6],[3,6],[3,5]],[[3,6],[3,6],[2,5]],[[3,6],[3,6],[2,4,6]],[[3,6],[3,6],[3,6]],[[3,5],[2,5],[2,4,6]],[[3,5],[2,5],[3,6]],[[3,5],[2,5],[3,5]],[[3,5],[2,5],[2,5]],[[3,5],[2,4,6],[3,6]],[[3,5],[2,4,6],[3,5]],[[3,5],[2,4,6],[2,5]],[[3,5],[2,4,6],[2,4,6]],[[3,5],[3,6],[3,5]],[[3,5],[3,6],[2,5]],[[3,5],[3,6],[2,4,6]],[[3,5],[3,6],[3,6]],[[3,5],[3,5],[2,5]],[[3,5],[3,5],[2,4,6]],[[3,5],[3,5],[3,6]],[[3,5],[3,5],[3,5]],[[2,5],[2,4,6],[3,6]],[[2,5],[2,4,6],[3,5]],[[2,5],[2,4,6],[2,5]],[[2,5],[2,4,6],[2,4,6]],[[2,5],[3,6],[3,5]],[[2,5],[3,6],[2,5]],[[2,5],[3,6],[2,4,6]],[[2,5],[3,6],[3,6]],[[2,5],[3,5],[2,5]],[[2,5],[3,5],[2,4,6]],[[2,5],[3,5],[3,6]],[[2,5],[3,5],[3,5]],[[2,5],[2,5],[2,4,6]],[[2,5],[2,5],[3,6]],[[2,5],[2,5],[3,5]],[[2,5],[2,5],[2,5]]]
ε              # Map all walls `y` to:
 ø             #  Zip/transpose; swapping rows and columns
 yí            #  Reverse each row in a wall `y`
   ø           #  Also zip/transpose those; swapping rows and columns
              #  Pair both
              #  For both:
              #   For each column:
    ü          #    For each pair of bricks in a column:
     Q         #     Check if they are equal to each other (1 if truthy; 0 if falsey)
    O          #    Then take the sum of these checked pairs for each column
   O           #   Take the sum of that entire column
   _           #   Then check which sums are exactly 0 (1 if 0; 0 if anything else)
   P           #   And check for which walls this is only truthy by taking the product
}O             # After the map: sum the resulting list
               # (and output it implicitly as result)
Кевин Круйссен
источник
0

Древесный уголь , 89 байт

Nθ⊞υ⟦⟧≔⟦⟧ηFυF⟦²¦³⟧«≧⁺∧Lι§ι⁰κ¿⁼κθ⊞ηι¿‹κθ⊞υ⁺⟦κ⟧ι»≔Eη⟦ι⟧ζF⊖N«≔ζι≔⟦⟧ζFιFη¿¬⊙§κ⁰№λμ⊞ζ⁺⟦λ⟧κ»ILζ

Попробуйте онлайн!Ссылка на подробную версию кода. Работает для прямоугольников размером до 12 на TIO, но может быть сделано примерно в три раза быстрее при стоимости 2 байта, если использовать битовое переворот вместо пересечения списка. Объяснение:

Nθ

Введите ширину.

⊞υ⟦⟧

Начните с ряда без кирпичей.

≔⟦⟧η

Начните с не завершенных строк.

Fυ

Цикл по рядам.

F⟦²¦³⟧«

Переверните кирпичи.

≧⁺∧Lι§ι⁰κ

Добавьте ширину кирпича к текущей ширине строки.

¿⁼κθ⊞ηι

Если это приводит к ширине ввода, добавьте эту строку в список завершенных строк.

¿‹κθ⊞υ⁺⟦κ⟧ι»

В противном случае, если это все еще меньше, чем ширина ввода, добавьте новую строку в список строк, таким образом вызывая ее для более поздней итерации.

≔Eη⟦ι⟧ζ

Составьте список стен одного ряда.

F⊖N«

Петля на один меньше высоты.

≔ζι

Сохраните список стен.

≔⟦⟧ζ

Очистить список стен.

Fι

Зациклите сохраненный список стен.

Fη

Цикл по законченным рядам.

¿¬⊙§κ⁰№λμ⊞ζ⁺⟦λ⟧κ»

Если ряд можно добавить к этой стене, добавьте его в список стен.

ILζ

Выведите длину окончательного списка стен.

Нил
источник