Azulejos de ladrillo únicos dentro de un rectángulo

13

Estaba navegando por Stackoverflow y vi esta pregunta sobre cómo colocar un mosaico en un rectángulo MxN, y pensé que sería genial para jugar al golf. Aquí está la tarea.

Dadas las dimensiones M y N, escriba un programa que muestre de cuántas maneras únicas puede rectificarse un rectángulo MxN (N es el número de filas, no columnas. No es que realmente importe) dadas estas restricciones.

  1. Todas las fichas son 2x1 o 3x1
  2. Todos los mosaicos permanecen dentro de su fila (es decir, todos son horizontales)
  3. Entre cada dos filas adyacentes, los mosaicos no deben alinearse, excepto en los dos extremos.
  4. M y N están garantizados para ser al menos 1

Por ejemplo, un mosaico válido de una matriz de 8x3 sería

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

Pero lo siguiente no sería válido, porque las filas se alinean

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

Casos de prueba:

8x3: 4

3x1: 1

1x1: 0

9x4: 10

Código de golf, por lo que gana la respuesta más corta.

rtpax
fuente
2
Su descripción del tamaño de los mosaicos parece usar una convención diferente del tamaño del rectángulo. ¿Son los azulejos en realidad 2x1o 3x1? ¿También es la salida para 4x1cero?
FryAmTheEggman
1
Bienvenido. Buen concepto de desafío, sin embargo, generalmente es mejor usar el sandbox para elaborar ideas de desafíos antes de publicarlas en main.
Beefster
@FryAmTheEggman Parece que OP ha intentado hacer que |s no contribuya a la longitud de la fila, mediante el uso de una representación como esta (donde, si no hay una tubería ( |), hay un espacio).
Erik the Outgolfer
1
La pregunta referenciada sobre SO ya no existe.
Arnauld

Respuestas:

5

Jalea , 20 bytes

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

Pruébalo en línea!

Erik el Outgolfer
fuente
Sé que la velocidad no era parte de la especificación, pero esto se agotó incluso en 11x10 cuando se ejecuta en tio. Me interesaría una explicación para entender por qué.
Nick Kennedy
@NickKennedy Esa es una entrada demasiado grande. Para el ancho 11, cada fila puede tener una de 9 inclinaciones diferentes. Para el ancho 11 y la altura 10, hay 9¹⁰ = 3486784401 posibles muros, incluidos los inválidos. Así funciona el poder cartesiano. Obviamente, TIO no tiene tiempo para dejar que mi solución calcule toda la matriz de paredes (se agota después de 60 segundos). Agregaré una explicación cuando tenga tiempo para hacerlo.
Erik the Outgolfer
Gracias. He mirado un poco la gelatina, pero en este momento me baso en explicaciones comentadas para entender lo que hace el código de las personas. Supuse que, dado el problema de tiempo, su código bruto fuerza la solución, que es una forma sensata de minimizar los requisitos del código.
Nick Kennedy
Por interés, he recreado en Jelly el método en mi código R usando la primera parte de su código. Está en Pruébelo en línea! y aunque es considerablemente más largo que el tuyo, maneja números más grandes. Tenga en cuenta que no maneja 1 fila correctamente en la actualidad. Sospecho que podría ser mucho más conciso, pero soy nuevo en Jelly.
Nick Kennedy
4

JavaScript (ES6), 119101106  96  91 bytes

(norte,METRO)

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

Pruébalo en línea!

Comentado

solFhsol

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
fuente
1

R , 243 231 bytes

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

Pruébalo en línea!

Versión con saltos de línea:

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

Tenga en cuenta que no hay recursión y maneja valores bastante grandes de myn (por ejemplo, 24x20 -> 3.3e19)

Aquí hay una respuesta comentada que funciona más o menos igual que la anterior, pero desactivé todas las funciones, por lo que en realidad es legible:

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)

El método para tomar una matriz y multiplicarla repetidamente por sí mismo proviene de una pregunta sobre stackoverflow . Este enfoque funciona aquí porque calcula efectivamente el número acumulado de ramas a través de las diferentes filas posibles de ladrillos.

Si se permiten paquetes externos, puedo bajarlo a 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)))
Nick Kennedy
fuente
1

Jalea , 26 bytes

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

Pruébalo en línea!

Desglosado:

Genere una lista de posibles muros como sumas acumulativas con el final eliminado:

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

Encuentre la tabla exterior de todos los muros posibles uno contra el otro que no tengan intersecciones:

œ&L¬ɗþ`

Lleve esta matriz al poder de (N-1) y luego sume todo:

æ*⁴’¤SS

Utiliza el primer bit de la respuesta de @ EriktheOutgolfer para generar la lista de posibles muros, y luego usa el enfoque de intersección de matriz y exponenciación de matriz de mi respuesta R. Como tal, funciona bien incluso con grandes N. Esta es mi primera respuesta de Jelly, y sospecho que se puede jugar más. Idealmente, también me gustaría cambiar la primera sección para que los requisitos de tiempo y memoria no escalen exponencialmente con M.

Nick Kennedy
fuente
0

05AB1E , 42 bytes

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

Estoy casi avergonzado de publicar esto, y definitivamente se puede jugar MUCHO con un enfoque diferente, pero como me llevó un tiempo completarlo, decidí publicarlo de todos modos y jugarlo desde aquí. El desafío parece más fácil de lo que es, pero definitivamente estoy usando un enfoque incorrecto aquí y tengo la sensación de que 05AB1E podría hacer alrededor de 25 bytes.

Pruébalo en línea. NOTA: No solo es largo, también es ineficiente, ya que el 9x4caso de prueba se ejecuta en aproximadamente 40 segundos en TIO.

Explicación:

Ŝ             # 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)
Kevin Cruijssen
fuente
0

Carbón , 89 bytes

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

Pruébalo en línea!El enlace es a la versión detallada del código. Funciona para rectángulos de un tamaño de hasta aproximadamente 12 en TIO, pero podría hacerse aproximadamente tres veces más rápido a un costo de 2 bytes mediante el giro de bits en lugar de la intersección de la lista. Explicación:

Nθ

Ingrese el ancho.

⊞υ⟦⟧

Comience con una fila sin ladrillos.

≔⟦⟧η

Comience sin filas completadas.

Fυ

Pase sobre las filas.

F⟦²¦³⟧«

Pase sobre los ladrillos.

≧⁺∧Lι§ι⁰κ

Agregue el ancho de ladrillo al ancho de fila actual.

¿⁼κθ⊞ηι

Si esto da como resultado el ancho de entrada, agregue esta fila a la lista de filas completadas.

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

De lo contrario, si esto es aún menor que el ancho de entrada, agregue la nueva fila a la lista de filas, lo que hará que sea recuperada por una iteración posterior.

≔Eη⟦ι⟧ζ

Haga una lista de paredes de una fila.

F⊖N«

Pase sobre uno menos que la altura.

≔ζι

Guarda la lista de paredes.

≔⟦⟧ζ

Borrar la lista de paredes.

Fι

Recorre la lista guardada de muros.

Fη

Pase sobre las filas completadas.

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

Si la fila se puede agregar a este muro, agréguelo a la lista de muros.

ILζ

Salida de la longitud de la lista final de paredes.

Neil
fuente