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 1
regiõ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 z
como abreviação para zipWith
raspar 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 l
executa metade das etapas de comparação e transposição. (.)>>=id
executa seu argumento duas vezes, sendo a forma sem pontos \f -> f.f
e 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 x
com a lista deslocada à direita 0:x
e 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, a
permaneceremos inalteradas
- Se
sgn(b) = 0
, temos a caixa de canto onde b
está o preenchimento e, portanto, a
permanece 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 a
e b
diferem, a*b
é menor ou igual a zero, enquanto a*a
é sempre maior que zero, então escolhemos o máximo e dividimos com a
para 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,
nub
elimina 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.