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 1und 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 1Regionen und die eindeutigen negativen Zahlen für die Anzahl der 0Regionen.
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 zals 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+1und 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 list die Vergleichsfunktion (siehe unten) und transpose.map lführt die Hälfte der Vergleichs- und Umsetzungsschritte aus. (.)>>=idführt sein Argument zweimal aus, wobei es \f -> f.fin diesem Fall die punktfreie Form von und um ein Byte kürzer ist , da die Regeln für die Rangfolge der Operatoren gelten.
list 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 xmit der rechtsverschobenen Liste 0:xund 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!bist 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, ableibt dies unverändert
- Wenn
sgn(b) = 0ja, haben wir den Eckfall, in dem bgepolstert wird und bleiben daher aunverä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 aund verschieden sind b, a*bkleiner oder gleich Null sind, während sie a*aimmer 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
nubDoppelte, 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