Haskell , 228 227 225 224 bytes
import Data.List
z=zipWith
a!b=div(max(a*a)(a*b))a
l x=z(!)(z(!)x(0:x))$tail x++[0]
s=(\x->length.($x).filter<$>[(>0),(<0)]).nub.(>>=id).(until=<<((==)=<<))((.)>>=id$transpose.map l).z(\i->z(\j x->2^i*j*(2*x-1))[1,3..])[1..]
Pruébalo en línea!
Explicación:
La idea para esta solución es la siguiente: Inicialice la matriz con valores únicos en cada celda, positivos para 1y negativos para 0. Luego, compare repetidamente cada celda con sus vecinos y, si el vecino tiene el mismo signo pero un número con un valor absoluto mayor, reemplace el número de la celda con el número del vecino. Una vez que esto llegue a un punto fijo, cuente el número de números positivos distintos para el número de 1regiones y los números negativos distintos para el número de 0regiones.
En codigo:
s=(\x->length.($x).filter<$>[(>0),(<0)]).nub.(>>=id).(until=<<((==)=<<))((.)>>=id$transpose.map l).z(\i->z(\j x->2^i*j*(2*x-1))[1,3..])[1..]
se puede separar en el preprocesamiento (asignación de números a las celdas), la iteración y el posprocesamiento (recuento de celdas)
Preprocesamiento
La parte de preprocesamiento es la función
z(\i->z(\j x->2^i*j*(2*x-1))[1,3..])[1..]
Que se usa zcomo abreviatura para zipWithrecortar algunos bytes. Lo que hacemos aquí es comprimir la matriz bidimensional con índices enteros en las filas e índices enteros impares en las columnas. Hacemos esto ya que podemos construir un entero único a partir de un par de enteros (i,j)usando la fórmula (2^i)*(2j+1). Si solo generamos enteros impares j, podemos omitir el cálculo de2*j+1 , ahorrando tres bytes.
Con el número único, ahora solo tenemos que multiplicar en un signo basado en el valor en la matriz, que se obtiene como 2*x-1
Iteración
La iteración se realiza por
(until=<<((==)=<<))((.)>>=id$transpose.map l)
Dado que la entrada tiene la forma de una lista de listas, realizamos la comparación vecina en cada fila, transponemos la matriz, realizamos la comparación en cada fila nuevamente (que debido a la transposición es lo que eran las columnas antes) y volvemos a transponer. El código que cumple uno de estos pasos es
((.)>>=id$transpose.map l)
donde lestá la función de comparación (detallada a continuación) y transpose.map lrealiza la mitad de los pasos de comparación y transposición. (.)>>=idrealiza su argumento dos veces, siendo la forma sin puntos de \f -> f.fy en un byte más corto en este caso debido a las reglas de precedencia del operador.
lse define en la fila de arriba como l x=z(!)(z(!)x(0:x))$tail x++[0]. Este código realiza un operador de comparación (!)(ver más abajo) en cada celda con primero su vecino izquierdo, y luego con su vecino derecho, comprimiendo la lista xcon la lista desplazada hacia la derecha 0:xy la lista desplazada hacia la izquierda tail x++[0]. Usamos ceros para rellenar las listas desplazadas, ya que nunca pueden ocurrir en la matriz preprocesada.
a!bse define en la fila de arriba como esto a!b=div(max(a*a)(a*b))a. Lo que queremos hacer aquí es la siguiente distinción de casos:
- Si
sgn(a) = -sgn(b), tenemos dos áreas opuestas en la matriz y no deseamos unificarlas, entonces apermanece sin cambios
- Si
sgn(b) = 0, tenemos el caso de la esquina donde bestá el relleno y por lo tanto apermanece sin cambios
- Si
sgn(a) = sgn(b), deseamos unificar las dos áreas y tomar la que tenga el valor absoluto más grande (por conveniencia).
Tenga en cuenta que sgn(a)nunca puede ser 0. Logramos esto con la fórmula dada. Si los signos de ay bdifieren, a*bes menor o igual a cero, mientras a*aque siempre es mayor que cero, entonces lo elegimos como el máximo y dividimos con apara volver a. De lo contrario, max(a*a)(a*b)es abs(a)*max(abs(a),(abs(b)), y al dividir esto entre a, obtenemos sgn(a)*max(abs(a),abs(b)), que es el número con el valor absoluto más grande.
Para iterar la función ((.)>>=id$transpose.map l)hasta que alcanza un punto fijo, utilizamos (until=<<((==)=<<)), que se toma de esta respuesta stackoverflow .
Postprocesamiento
Para el posprocesamiento, utilizamos la parte
(\x->length.($x).filter<$>[(>0),(<0)]).nub.(>>=id)
que es solo una colección de pasos.
(>>=id)aplasta la lista de listas en una sola lista,
nubelimina los dobles,
(\x->length.($x).filter<$>[(>0),(<0)])divide la lista en un par de listas, una para números positivos y otra para números negativos, y calcula su longitud.
[[1,0];[0,1]]para asegurarse de que la conectividad diagonal no esté incluida