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..]
Experimente online!
Explicação:
A idéia para esta solução é a seguinte: Inicialize a matriz com valores exclusivos em cada célula, positivos para 1 e negativos para 0. Em seguida, compare repetidamente cada célula com seus vizinhos e, se o vizinho tiver o mesmo sinal, mas um número com um valor absoluto maior, substitua o número da célula pelo número do vizinho. Quando isso atingir um ponto fixo, conte o número de números positivos distintos para o número de 1regiões e os números negativos distintos para o número de0 regiões.
Em código:
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..]
pode ser separado no pré-processamento (atribuindo números às células), na iteração e no pós-processamento (contagem de células)
Pré-processando
A parte de pré-processamento é a função
z(\i->z(\j x->2^i*j*(2*x-1))[1,3..])[1..]
Que usa zcomo abreviação para zipWithraspar alguns bytes. O que fazemos aqui é compactar a matriz bidimensional com índices inteiros nas linhas e índices inteiros ímpares nas colunas. Fazemos isso, pois podemos construir um número inteiro único a partir de um par de números inteiros (i,j)usando a fórmula (2^i)*(2j+1). Se gerarmos números inteiros ímpares para j, podemos pular o cálculo da2*j+1 , economizando três bytes.
Com o número único, agora precisamos multiplicar apenas um sinal com base no valor da matriz, que é obtido como 2*x-1
Iteração
A iteração é feita por
(until=<<((==)=<<))((.)>>=id$transpose.map l)
Como a entrada está na forma de uma lista de listas, realizamos a comparação de vizinhos em cada linha, transpomos a matriz, realizamos a comparação em cada linha novamente (que devido à transposição é o que eram as colunas anteriores) e transpomos novamente. O código que realiza uma dessas etapas é
((.)>>=id$transpose.map l)
onde lé a função de comparação (detalhada abaixo) e transpose.map lexecuta metade das etapas de comparação e transposição. (.)>>=idexecuta seu argumento duas vezes, sendo a forma sem pontos \f -> f.fe um byte mais curta nesse caso, devido às regras de precedência do operador.
lé definido na linha acima como l x=z(!)(z(!)x(0:x))$tail x++[0]. Esse código executa um operador de comparação (!)(veja abaixo) em todas as células com o primeiro vizinho esquerdo e, depois, com o vizinho direito, fechando a lista xcom a lista deslocada à direita 0:xe a lista deslocada à esquerdatail x++[0] . Usamos zeros para preencher as listas deslocadas, pois elas nunca podem ocorrer na matriz pré-processada.
a!bé definido na linha acima disso como a!b=div(max(a*a)(a*b))a. O que queremos fazer aqui é a seguinte distinção entre casos:
- Se
sgn(a) = -sgn(b)tivermos duas áreas opostas na matriz e não desejarmos unificá-las, apermaneceremos inalteradas
- Se
sgn(b) = 0, temos a caixa de canto onde bestá o preenchimento e, portanto, apermanece inalterada
- Se
sgn(a) = sgn(b)desejarmos unificar as duas áreas e escolher a que tiver o maior valor absoluto (por conveniência).
Note que sgn(a)nunca pode ser 0. Conseguimos isso com a fórmula dada. Se os sinais de ae bdiferem, a*bé menor ou igual a zero, enquanto a*aé sempre maior que zero, então escolhemos o máximo e dividimos com apara voltar a. Caso contrário, max(a*a)(a*b)é abs(a)*max(abs(a),(abs(b)), e dividindo isso por a, obtemos sgn(a)*max(abs(a),abs(b)), que é o número com o valor absoluto maior.
Para iterar a função ((.)>>=id$transpose.map l)até que ela atinja um ponto fixo, usamos o (until=<<((==)=<<))que é retirado dessa resposta do stackoverflow .
Pós-processamento
Para pós-processamento, usamos a parte
(\x->length.($x).filter<$>[(>0),(<0)]).nub.(>>=id)
que é apenas uma coleção de etapas.
(>>=id)esmaga a lista de listas em uma única lista,
nubelimina duplas,
(\x->length.($x).filter<$>[(>0),(<0)])divide a lista em um par de listas, uma para números positivos e outra para números negativos, e calcula seus comprimentos.
[[1,0];[0,1]]para garantir que a conectividade diagonal não esteja incluída.