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 1
y 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 1
regiones y los números negativos distintos para el número de 0
regiones.
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 z
como abreviatura para zipWith
recortar 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 l
está la función de comparación (detallada a continuación) y transpose.map l
realiza la mitad de los pasos de comparación y transposición. (.)>>=id
realiza su argumento dos veces, siendo la forma sin puntos de \f -> f.f
y en un byte más corto en este caso debido a las reglas de precedencia del operador.
l
se 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 x
con la lista desplazada hacia la derecha 0:x
y 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!b
se 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 a
permanece sin cambios
- Si
sgn(b) = 0
, tenemos el caso de la esquina donde b
está el relleno y por lo tanto a
permanece 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 a
y b
difieren, a*b
es menor o igual a cero, mientras a*a
que siempre es mayor que cero, entonces lo elegimos como el máximo y dividimos con a
para 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,
nub
elimina 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