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..]
Probieren Sie es online!
Erläuterung:
Die Idee für diese Lösung lautet wie folgt: Initialisieren Sie die Matrix mit eindeutigen Werten in jeder Zelle, positiv für 1
und negativ für 0
. Vergleichen Sie dann wiederholt jede Zelle mit ihren Nachbarn und ersetzen Sie die Zellennummer durch die Nummer des Nachbarn, wenn der Nachbar das gleiche Vorzeichen, aber eine Nummer mit einem größeren absoluten Wert hat. Sobald dies einen festen Punkt erreicht, zählen Sie die Anzahl der eindeutigen positiven Zahlen für die Anzahl der 1
Regionen und die eindeutigen negativen Zahlen für die Anzahl der 0
Regionen.
In Code:
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..]
kann in die Vorverarbeitung (Zuweisen von Zahlen zu Zellen), die Iteration und die Nachverarbeitung (Zählen von Zellen) unterteilt werden
Vorverarbeitung
Der Vorverarbeitungsteil ist die Funktion
z(\i->z(\j x->2^i*j*(2*x-1))[1,3..])[1..]
Wofür z
als Abkürzung verwendet wird zipWith
, um ein paar Bytes abzukürzen . Was wir hier tun, ist das zweidimensionale Array mit Integer-Indizes in den Zeilen und ungeraden Integer-Indizes in den Spalten zu komprimieren. Wir tun dies, da wir (i,j)
mithilfe der Formel eine eindeutige Ganzzahl aus einem Paar von Ganzzahlen erstellen können (2^i)*(2j+1)
. Wenn wir nur ungerade Ganzzahlen für generieren j
, können wir die Berechnung überspringen 2*j+1
und drei Bytes einsparen.
Mit der eindeutigen Zahl müssen wir nur noch ein Vorzeichen multiplizieren, das auf dem Wert in der Matrix basiert, die erhalten wird als 2*x-1
Iteration
Die Iteration erfolgt durch
(until=<<((==)=<<))((.)>>=id$transpose.map l)
Da die Eingabe in Form einer Liste von Listen erfolgt, führen wir den Nachbarvergleich für jede Zeile durch, transponieren die Matrix, führen den Vergleich für jede Zeile erneut durch (was aufgrund der Transponierung die vorherigen Spalten ist) und transponieren erneut. Der Code, der einen dieser Schritte ausführt, lautet
((.)>>=id$transpose.map l)
Wo l
ist die Vergleichsfunktion (siehe unten) und transpose.map l
führt die Hälfte der Vergleichs- und Umsetzungsschritte aus. (.)>>=id
führt sein Argument zweimal aus, wobei es \f -> f.f
in diesem Fall die punktfreie Form von und um ein Byte kürzer ist , da die Regeln für die Rangfolge der Operatoren gelten.
l
ist in der obigen Zeile definiert als l x=z(!)(z(!)x(0:x))$tail x++[0]
. Dieser Code führt einen Vergleichsoperator (!)
(siehe unten) für jede Zelle mit zuerst ihrem linken Nachbarn und dann mit ihrem rechten Nachbarn durch, indem die Liste x
mit der rechtsverschobenen Liste 0:x
und der linksverschobenen Liste nacheinander gezippt wird tail x++[0]
. Wir verwenden Nullen, um die verschobenen Listen aufzufüllen, da sie in der vorverarbeiteten Matrix niemals auftreten können.
a!b
ist in der Zeile darüber definiert als a!b=div(max(a*a)(a*b))a
. Was wir hier machen wollen, ist die folgende Fallunterscheidung:
- Wenn
sgn(a) = -sgn(b)
wir zwei gegenüberliegende Bereiche in der Matrix haben und diese nicht vereinheitlichen möchten, a
bleibt dies unverändert
- Wenn
sgn(b) = 0
ja, haben wir den Eckfall, in dem b
gepolstert wird und bleiben daher a
unverändert
- Wenn
sgn(a) = sgn(b)
ja, möchten wir die beiden Bereiche vereinheitlichen und den Bereich mit dem größeren absoluten Wert verwenden (der Einfachheit halber).
Beachten Sie, dass sgn(a)
es niemals sein kann 0
. Dies erreichen wir mit der angegebenen Formel. Wenn die Vorzeichen von a
und verschieden sind b
, a*b
kleiner oder gleich Null sind, während sie a*a
immer größer als Null sind, wählen wir sie als Maximum und teilen sie mit a
, um zurück zu kommen a
. Andernfalls max(a*a)(a*b)
ist abs(a)*max(abs(a),(abs(b))
, und dies wird durch Division durch a
, bekommen wir sgn(a)*max(abs(a),abs(b))
, was die Zahl mit dem größeren Absolutwert ist.
Um die Funktion zu iterieren, ((.)>>=id$transpose.map l)
bis sie einen festen Punkt erreicht, verwenden wir (until=<<((==)=<<))
, der aus dieser Stapelüberlaufantwort entnommen wird .
Nachbearbeitung
Für die Nachbearbeitung verwenden wir das Teil
(\x->length.($x).filter<$>[(>0),(<0)]).nub.(>>=id)
Das ist nur eine Ansammlung von Schritten.
(>>=id)
Zerquetscht die Liste der Listen in eine einzelne Liste, entfernt
nub
Doppelte, unterteilt
(\x->length.($x).filter<$>[(>0),(<0)])
die Liste in zwei Listen, eine für positive und eine für negative Zahlen, und berechnet deren Länge.
[[1,0];[0,1]]
, um sicherzustellen, dass keine diagonale Konnektivität enthalten ist