¿Puedo separar el rompecabezas?

38

Escriba un programa o función que tome una cuadrícula de texto rectangular donde cada celda sea una Ao una B. Todas las Aceldas formarán una forma simplemente conectada , es decir, todas estarán conectadas ortogonalmente sin agujeros (las letras adyacentes en diagonal no cuentan como conectadas). Del mismo modo, todas las Bceldas formarán otra forma simplemente conectada. La cuadrícula siempre contendrá al menos uno Ay al menos uno B.

Imagine que la cuadrícula es en realidad dos piezas de plástico delgado en forma de bloque, representadas por las porciones Ay B. Si se colocara plana sobre una mesa, ¿podrían separarse las dos piezas mientras se mantienen ambas completamente planas sobre la mesa?

Imprima o devuelva un valor verdadero si las dos Ay las Bformas se pueden separar de esta manera simplemente separándolas. De lo contrario, imprima o devuelva un valor falso .

Por ejemplo, la entrada

AAA
ABB
AAA

es cierto porque la BBsección se puede deslizar hacia la derecha, separándola de la A's:

AAA
A    BB
AAA

Sin embargo, la entrada

AAAA
ABBA
ABAA

es falso porque no hay forma de separar las porciones Ay Bsin que se superpongan.

El código más corto en bytes gana. Si lo desea, puede usar cualquiera de los dos caracteres ASCII imprimibles en lugar de Ay B.

Ejemplos de verdad (separados por líneas vacías)

BBB
BAA
BBB

BA

A
B

AB
AB

AAA
BBB

AAAAB
ABBBB

ABBA
ABBA
AAAA

AAAAAABBBBBBBBB
AABBBBBBBBBBBBB
AAAAAAAAAABBBBB
AABBBBBBBBBBBBB
AAAAAAAAAAAAAAB

AAAAAAAAAAAA
ABABABABABAB
BBBBBBBBBBBB

BAAAAABB
BBAAABBB
BBBABBBB
BBBABBBB
BBBABBBB
BBBBBBBB
BBBBBBBB

AAA
BAA
AAA

Ejemplos de falsa

BBBB
BAAB
BABB

BBBB
BAAB
AABB

BBBBBBB
BBBBBAB
AAAAAAB
BBBBBBB

BAAA
BABA
BBBA
AABA
AAAA

AAAAAAA
ABBBBBA
AAAAABA
BBBBBBA

BAAAAABB
BBAAABBB
BBBABBBB
BBBABBBB
BBBAABBB
BBBBBBBB
BBBBBBBB

AAA
ABA
BBA
ABA
AAA
Pasatiempos de Calvin
fuente

Respuestas:

9

Caracoles, 14

o(t\B+~)+!(t\B

Si el rompecabezas se puede separar, imprime el área de la entrada. De lo contrario, imprime 0.

Es un poco lento para los ejemplos más grandes, ya que lleva tiempo factorial en el área de la cuadrícula.

         ,, the program will print the number of starting cells matching this pattern
o        ,, pick a cardinal direction
(
    t    ,, teleport to any cell on the grid
    \B+  ,, match "B" 1 or more times, moving in the direction set by 'o'.
         ,, when a cell is matched, it gets slimed and can't be matched again.
    ~    ,, match an out-of-bounds cell
)+       ,, do parenthesized instructions 1 or more times
!(       ,, the following must not match:
    t\B  ,, teleport to some cell and match 'B'
Feersum
fuente
44
"Es un poco lento .." . No estoy seguro de lo que esperaba de un idioma llamado Snails ...
Bassdrop Cumberwubwubwub
44
@Bas Ahora ahora, no hay razón para frotar sal en las heridas.
Trasiva
21

CJam, 33 32 20 19 17 bytes

Versión revisada, con soporte masivo de @ Sp3000 y @ MartinBüttner:

qN/_z]{:e`z,3<}/|

Pruébalo en línea

Contribuciones

  • @ Sp3000 sugirió una simplificación crítica de mi algoritmo original.
  • @ MartinBüttner aplicó sus locas habilidades de golf al enfoque revisado, que casi con certeza resultó en un código más corto de lo que hubiera llegado incluso después de considerar la simplificación.

Algoritmo y Prueba

A continuación se explican los criterios para que el rompecabezas se deslice horizontalmente. El caso vertical puede determinarse mirando columnas en lugar de filas, o transponiendo la matriz de caracteres y mirando las filas nuevamente.

Usaré el término "estirar" para una secuencia máxima de las mismas letras. Por ejemplo, las siguientes filas tienen 1, 2 y 3 tramos respectivamente:

AAAAAAAA
BBBAAAAA
AABBBAAA

También usaré el término "enclavado" para una fila / rompecabezas que no puede separarse.

La observación clave es que el rompecabezas puede separarse si y solo si todas las filas tienen como máximo 2 tramos . O invertido, se enclava si y solo si hay alguna fila con más de 2 tramos .

Lo siguiente podría no calificar como una prueba matemática estricta, pero creo que es una explicación convincente de por qué este tiene que ser el caso.

Es fácil ver que el rompecabezas está enclavado si tiene filas de más de 2 tramos. Mirando una fila con 3 tramos:

BBBAAB

Está claro que evita que el rompecabezas se separe porque el Atramo está bloqueado entre los Btramos. Esto significa que la fila está enclavada, lo que a su vez hace que todo el rompecabezas esté enclavado.

La dirección opuesta de la prueba no es tan obvia. Necesitamos demostrar que no hay acertijos entrelazados donde todas las filas tengan solo 1 o 2 tramos. Comenzando con un par de observaciones:

  • Las filas con solo 1 tramo no contribuyen a que un rompecabezas se entrelace, ya que pueden deslizarse en cualquier dirección sin colisiones.
  • Si todas las filas con 2 tramos tienen el mismo orden de Ay B, el rompecabezas claramente no está enclavado. En este caso, todas las Aceldas quedan de todas las Bceldas, o viceversa, y no hay colisiones al separar las dos piezas.

El único caso complicado serían los rompecabezas donde tenemos filas con 2 tramos de diferente orden. Voy a mostrar que tales rompecabezas no existen bajo las especificaciones dadas. Para mostrar esto, veamos un rompecabezas parcial que tiene esta configuración, donde .están los comodines:

.......
AAABBBB
.......
BBAAAAA
.......

Ahora, la especificación dice que tanto el Ay Blas células se conectan simplemente en todos los puzzles válidos. Para hacer que las Aceldas se conecten en el rompecabezas parcial anterior, tenemos dos opciones:

  1. Damos vueltas alrededor de uno de los tramos de B, por ejemplo:

    ..AAAAAA
    AAABBBBA
    .......A
    BBAAAAAA
    ........
    

    Para hacer esto, inevitablemente extendemos una de las filas para tener 3 tramos, por lo que esto nunca nos dará un rompecabezas válido donde todas las filas tengan como máximo 2 tramos.

  2. Los conectamos por un camino directo:

    .......
    AAABBBB
    ..A....
    BBAAAAA
    .......
    

    Las Aceldas ahora están simplemente conectadas y todavía no hay filas con más de 2 tramos. Sin embargo, las Bcélulas también necesitan estar simplemente conectadas. La ruta directa ahora está bloqueada por las Aceldas conectadas , y la única forma de conectar las Bceldas es hacer un bucle alrededor de uno de los tramos de las Aceldas. Esto nos lleva de vuelta al caso 1, donde no podemos hacer eso sin crear filas de 3 tramos.

Para contar los tramos, la implementación utiliza el operador CJam RLE.

Explicación del Código

qN/     Get input and split at newlines.
_z      Make a transposed copy.
]       Wrap the original and transposed puzzle in an array so that we can
        loop over the two.
{       Start of loop over original and transposed puzzle.
  :e`     Apply RLE to all rows.
  z,      Transpose the matrix with the RLE rows, and take the element count of the
          result. Or in other words, take the column count. This will be the length
          of the longest row after RLE.
  3<      Check the length for less than 3.
}/      End of loop over original and transposed puzzle.
|       Or the results of the two.
Reto Koradi
fuente
9

JavaScript (ES6), 108 107 98 91 82 bytes

a=>!(T=[],R=/AB+A|BA+B/).test([...a].map((c,i)=>T[i%-~a.search`
`]+=c))|!R.test(a)

Demostración en vivo . Probado en Firefox. Toma la entrada como una cadena delimitada por nueva línea.

Ediciones:

  • Se guardó 1 byte al cambiar \na una nueva línea literal.
  • Ahorró 9 bytes al hacer la prueba RegExp directamente en la cadena de varias líneas en lugar de convertirla en una matriz.
  • ¡Se eliminaron otros 9 bytes usando comprensiones de matriz para dividir cadenas, moviéndose! en gfunción y llamando a RegExp directamente en la matriz en lugar de usar find.
  • Continuó la secuencia artmética guardando otros 9 bytes. Hizo un módulo en el índice en lugar de dividir la matriz por nuevas líneas antes de tomar la transposición.

Cómo funciona

Versión previa:

a=>(T=[],a.split`
`.map(s=>s.split``.map((c,i)=>T[i]+=c)),!T.find(g=s=>/AB+A|BA+B/.test(s)))|!g(a)
  1. Tome la entrada ay divídala por nuevas líneas en una serie de cadenas.
  2. Transponer ay almacenar en T. Use mappara iterar sobre cada elemento de a, divida la cadena en una matriz de caracteres, y use mapnuevamente para agregar el icarácter th en la línea a la ilínea th de T. Como cada elemento de Tno está inicializado, terminará pareciendo algo así "undefinedAAABBA", pero esto no importará.
  3. Cree una función de prueba basada en RegExp gque coincida con el patrón /AB+A|BA+B/. Si coincide, las piezas se bloquean en la dirección dada porque entonces hay un conjunto de Bs intercalados entre dos o más Aso o viceversa.
  4. Use la función gde prueba para probar todos los elementos del bloque ay su transposición Tpara una coincidencia usando find. Si ambos coinciden, las piezas se bloquean en ambas direcciones, por lo que se genera un valor falso, de lo contrario, uno verdadero.
codificador intrépido
fuente
5

Javascript (ES6), 118

slidey=
// code
a=>!a.match(R=/AB+A|BA+B/)||!(a=a.split`
`.map(b=>b.split``))[0].map((_,c)=>a.map(d=>d[c])).some(e=>e.join``.match(R))

// IO
var S =document.getElementById('S');
S.onkeyup = _=> document.getElementById('P').innerText = slidey(S.value);

document.getElementById('P').innerText = slidey(S.value);
<textarea id='S'>BAAAAABB
BBAAABBB
BBBABBBB
BBBABBBB
BBBABBBB
BBBBBBBB
BBBBBBBB</textarea>
<p id='P'></p>

Explicación:

a=> !/* check string horizontally */ || !/* check string vertically by transposing it and
                                            running the same horizontal check */

a=> !a.match(R=/AB+A|BA+B/) || !/* ... */
// check for lines containing something like BAAAAAB or ABBBBBBBA
// this is the only way something can get blocked horizontally
// eg AAAAAAA
//    AAABAAA <<< note the B in the middle of As here
//    AAABBBB <<< blocked from being pulled out horizontally
//    AAAAAAA

a=> /* ... */ ||!( a = a.split('\n').map(b=> b.split('')) ) // split a into 2D array
    [0].map((_,c)=>a.map(d=>d[c])) // transpose it
    .some(e=>e.join``.match(R)) // run the check again using `some` to go line by line
                                // which is shorter than .join().match() outside

a=> !/* returns null if no horizontal obstacles and an array if there are */
    || !/* same thing */
// negate both to cast to a boolean (false if obstacles, true if not)
// an input can only be unslidable if both directions are blocked
// so (no obstacles vertically? || no obstacles horizontally?) gives the answer
DankMemes
fuente
Ratas! Gáname a eso.
intrepidcoder
5

JavaScript (ES6) 72 74

Editar 2 bytes guardados gracias a @NotthatCharles

Tengo la comprensión intuitiva de que si una pieza puede deslizarse solo una fracción de paso, entonces es gratis. Los casos de prueba actuales confirman esto.

Así que verifico solo un paso en cada dirección.

Caracteres utilizados: 1 y 0
2 bytes más para usar 2 caracteres imprimibles como A y B

Pruebe a ejecutar el fragmento a continuación en un navegador compatible con EcmaScript 6 (compatible con el operador de propagación: IE Firefox)

f=s=>[w=~s.search`
`,-w,-1,1].some(o=>![...s].some((x,p)=>x+s[p+o]==10))

// 4 bytes more- for any symbol, not just 1 and 0 (for instance A and B):
g=s=>[w=~s.search`
`,-w,-1,1].some(o=>![...s].some((x,p)=>x+s[p+o]=='AB'))

//TEST
console.log=x=>O.innerHTML+=x+'\n'

testOk = [
 '111\n100\n111',
 '10',
 '0\n1',
 '01\n01',
 '000\n111',
 '00001\n01111',
 '0110\n0110\n0000',
 '000000111111111\n001111111111111\n000000000011111\n001111111111111\n000000000000001',
 '000000000000\n010101010101\n111111111111',
 '10000011\n11000111\n11101111\n11101111\n11101111\n11111111\n11111111',
 '000\n100\n000'
]

testKo = [
 '1111\n1001\n1011',
 '1111\n1001\n0011',
 '1111111\n1111101\n0000001\n1111111',
 '1000\n1010\n1110\n0010\n0000',
 '0000000\n0111110\n0000010\n1111110',
 '10000011\n11000111\n11101111\n11101111\n11100111\n11111111\n11111111',
 '000\n010\n110\n010\n000'
]

console.log('Expecting true')
testOk.forEach(t=>console.log(t+'\n'+f(t)+'\n'))
console.log('Expecting false')
testKo.forEach(t=>console.log(t+'\n'+f(t)+'\n'))
<pre id=O></pre>

edc65
fuente
Bueno, eso es solo genio. Golpeado nuevamente por un profesional. :-)
ETHproductions
s[p+o]=='0'Parece un poco largo. ¿Podría ser reemplazado con 1-s[p+o], o al menos s[p+o]==0?
ETHproductions
@ETHproductions sí, es largo, vale la pena pensar un poco más. Debe dar falso para '\ n' (borde vertical, convierte a 0) y para indefinido (borde superior e inferior, convierte a NaN)
edc65
=='A'puede ser sustituido por <'B'. Lo mismo para=='B'
No es que Charles
Además, ¿no puedes simplemente hacerlo x+s[p+o]=='AB'?
No es que Charles
3

Mathematica 100 69 bytes

Con 31 bytes masivos guardados, gracias a @Martin Buttner,

g=Max[Length/@Split/@#]<3&;g[c=Characters@StringSplit@#]||g@Thread@c&

Formatea la entrada como una matriz de caracteres; También hace una transposición de la matriz. Si la matriz o su transposición no tiene más de 2 corridas por fila, entonces el rompecabezas puede deslizarse.

{a,a,b,b,b} Tiene 2 corridas de letras.

{a,a,b,a,a} Tiene 3 corridas de letras.

{a,a,b,a,a,a,b,b,b,b,b,b,b,b} Tiene 4 corridas de letras.

DavidC
fuente
2

Dyalog APL, 22 bytes

(∨/{∧/2>+/2≠/⍵}¨)⊂∘⍉,⊂

Pruébalo aquí Esta es una función que toma una matriz 2D de caracteres y devuelve 1las instancias deslizantes y las 0no deslizantes. El algoritmo es similar a la mayoría de las otras respuestas: verifique la matriz y su transposición de que ninguna fila contenga más de un par adyacente de letras diferentes. Para la matriz de entrada 4x3

AAAA
ABBB
AAAB

la función se puede invocar como

f ← (∨/{∧/2>+/2≠/⍵}¨)⊂∘⍉,⊂
f 4 3 ⍴ 'AAAAABBBAAAB'

que da como resultado 1.

Explicación

⊂∘⍉,⊂   The matrix and its transpose.
{...}¨   For each of them:
  2≠/⍵   On each row, replace each adjacent pair with 1 if they differ, with 0 otherwise
  2>+/    Take the sum on each row and check that it's less than 2
  ∧/     AND over all rows
∨/      OR over the resulting two values
Zgarb
fuente
1

JavaScript (ES6), 94 bytes

x=>!(y=/AB+A|BA+B/).test(x)|(z=[],x.split`
`.map(b=>b.split``.map((c,i)=>z[i]+=c)),!y.test(z))

Método alternativo del mismo tamaño:

x=>(t=s=>!/AB+A|BA+B/.test(s),z=[],x.split`
`.map(b=>b.split``.map((c,i)=>z[i]+=c)),t(x)|t(z))

Esto regresa 1para una entrada veraz y 0falsa. Empecé a trabajar en esto antes de que se publicaran otras respuestas. Originalmente también intenté usar las comprensiones de matriz de ES7, pero eso terminó aproximadamente 10 caracteres más que este método.

Pruébalo:

a=x=>!(y=/AB+A|BA+B/).test(x)|(z=[],x.split`
`.map(b=>b.split``.map((c,i)=>z[i]+=c)),!y.test(z))

P.onclick=_=>Q.innerHTML='Result: '+a(O.value)
<textarea id=O cols="20" rows="8">AAAAAABBBBBBBBB
AABBBBBBBBBBBBB
AAAAAAAAAABBBBB
AABBBBBBBBBBBBB
AAAAAAAAAAAAAAB</textarea>
<button id=P>Test</button>
<p id=Q>Result: </p>

Sugerencias bienvenidas!

ETHproducciones
fuente
Usar [... b] en lugar de b.split`` ahorra 3 bytes, pero solo funciona en algunos navegadores.
intrepidcoder