Haskell , 228 227 225 224 byte
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..]
Provalo online!
Spiegazione:
L'idea per questa soluzione è la seguente: Inizializza la matrice con valori univoci in ogni cella, positiva per 1
e negativa per 0
. Quindi confronta ripetutamente ogni cella con i suoi vicini e, se il vicino ha lo stesso segno ma un numero con un valore assoluto più grande, sostituisci il numero della cella con il numero del vicino. Una volta che questo raggiunge un punto fisso, conta il numero di numeri positivi distinti per il numero di 1
regioni e i numeri negativi distinti per il numero di0
regioni.
Nel codice:
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..]
può essere separato nella preelaborazione (assegnazione dei numeri alle celle), nell'iterazione e nella postelaborazione (conteggio delle celle)
Pre-elaborazione
La parte di preelaborazione è la funzione
z(\i->z(\j x->2^i*j*(2*x-1))[1,3..])[1..]
Che usa z
come abbreviazione per zipWith
radere qualche byte. Quello che facciamo qui è comprimere l'array bidimensionale con indici di numero intero nelle righe e indici di numero intero dispari nelle colonne. Lo facciamo poiché possiamo costruire un numero intero univoco da una coppia di numeri interi (i,j)
usando la formula (2^i)*(2j+1)
. Se generiamo solo numeri dispari per j
, possiamo saltare il calcolo 2*j+1
, salvando tre byte.
Con il numero univoco, ora non ci resta che moltiplicare per un segno basato sul valore nella matrice, che si ottiene come 2*x-1
Iterazione
L'iterazione è eseguita da
(until=<<((==)=<<))((.)>>=id$transpose.map l)
Poiché l'input ha la forma di un elenco di elenchi, eseguiamo il confronto adiacente su ogni riga, trasponiamo la matrice, eseguiamo nuovamente il confronto su ogni riga (che a causa del recepimento è ciò che era le colonne prima) e trasponiamo nuovamente. Il codice che compie uno di questi passaggi è
((.)>>=id$transpose.map l)
dove si l
trova la funzione di confronto (dettagliata di seguito) ed transpose.map l
esegue la metà delle fasi di confronto e trasposizione. (.)>>=id
esegue il suo argomento due volte, essendo la forma senza punti di \f -> f.f
e di un byte più breve in questo caso a causa delle regole di precedenza dell'operatore.
l
è definito nella riga sopra come l x=z(!)(z(!)x(0:x))$tail x++[0]
. Questo codice esegue un operatore di confronto (!)
(vedi sotto) su ogni cella con prima il suo vicino sinistro, e poi con il suo vicino destro, comprimendo l'elenco x
con l'elenco spostato a destra 0:x
e l'elenco spostato a sinistra tail x++[0]
a sua volta. Usiamo gli zero per riempire gli elenchi spostati, poiché non possono mai verificarsi nella matrice preelaborata.
a!b
è definito nella riga sopra questo come a!b=div(max(a*a)(a*b))a
. Ciò che vogliamo fare qui è la seguente distinzione tra i casi:
- Se
sgn(a) = -sgn(b)
abbiamo due aree opposte nella matrice e non desideriamo unificarle, quindi a
rimane invariata
- Se
sgn(b) = 0
, abbiamo la custodia ad angolo in cui b
è imbottitura e quindi a
rimane invariata
- Se
sgn(a) = sgn(b)
, desideriamo unificare le due aree e prendere quella con il valore assoluto più grande (per comodità).
Nota che sgn(a)
non può mai essere 0
. Lo realizziamo con la formula fornita. Se i segni di a
e b
differiscono, a*b
è minore o uguale a zero, mentre a*a
è sempre maggiore di zero, quindi lo scegliamo come massimo e dividiamo con a
per tornare indietro a
. Altrimenti, max(a*a)(a*b)
è abs(a)*max(abs(a),(abs(b))
, e dividendolo per a
, otteniamo sgn(a)*max(abs(a),abs(b))
, che è il numero con il valore assoluto più grande.
Per iterare la funzione ((.)>>=id$transpose.map l)
fino a quando non raggiunge un punto fisso, usiamo (until=<<((==)=<<))
, che è preso da questa risposta di StackOverflow .
Post produzione
Per la postelaborazione, utilizziamo la parte
(\x->length.($x).filter<$>[(>0),(<0)]).nub.(>>=id)
che è solo una raccolta di passaggi.
(>>=id)
comprime l'elenco degli elenchi in un unico elenco,
nub
elimina i doppi,
(\x->length.($x).filter<$>[(>0),(<0)])
suddivide l'elenco in una coppia di elenchi, uno per i numeri positivi e uno per i numeri negativi, e calcola le loro lunghezze.
[[1,0];[0,1]]
per assicurarti che la connettività diagonale non sia inclusa