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..]
Cobalah online!
Penjelasan:
Gagasan untuk solusi ini adalah sebagai berikut: Menginisialisasi matriks dengan nilai unik di setiap sel, positif untuk 1dan negatif untuk 0. Kemudian berulang kali membandingkan setiap sel dengan tetangganya dan, jika tetangganya memiliki tanda yang sama tetapi nomor dengan nilai absolut yang lebih besar, ganti nomor sel dengan nomor tetangga. Setelah ini mencapai titik tetap, hitung jumlah angka positif berbeda untuk jumlah 1wilayah dan angka negatif berbeda untuk jumlah0 wilayah.
Dalam kode:
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..]
dapat dipisahkan menjadi preprocessing (menetapkan angka ke sel), iterasi, dan postprocessing (menghitung sel)
Preprocessing
Bagian preprocessing adalah fungsinya
z(\i->z(\j x->2^i*j*(2*x-1))[1,3..])[1..]
Yang digunakan zsebagai singkatan untuk zipWithmencukur beberapa byte. Apa yang kita lakukan di sini adalah zip array dua dimensi dengan indeks integer di baris dan indeks integer ganjil di kolom. Kami melakukan ini karena kami dapat membangun bilangan bulat unik dari sepasang bilangan bulat (i,j)menggunakan rumus (2^i)*(2j+1). Jika kami hanya menghasilkan bilangan bulat ganjil untuk j, kami dapat melewati penghitungan2*j+1 , menghemat tiga byte.
Dengan nomor unik, kita sekarang hanya perlu mengalikan tanda berdasarkan nilai dalam matriks, yang diperoleh sebagai 2*x-1
Perulangan
Iterasi dilakukan oleh
(until=<<((==)=<<))((.)>>=id$transpose.map l)
Karena inputnya dalam bentuk daftar daftar, kami melakukan perbandingan tetangga pada setiap baris, mengubah matriks, melakukan perbandingan pada setiap baris lagi (yang karena transpose adalah apa yang menjadi kolom sebelumnya) dan mentranspos lagi. Kode yang menyelesaikan salah satu dari langkah-langkah ini adalah
((.)>>=id$transpose.map l)
di mana lfungsi perbandingan (dirinci di bawah) dan transpose.map lmelakukan setengah dari langkah perbandingan dan transposisi. (.)>>=idmelakukan argumennya dua kali, menjadi bentuk pointfree \f -> f.fdan lebih pendek satu byte dalam hal ini karena aturan prioritas operator.
ldidefinisikan pada baris di atas sebagai l x=z(!)(z(!)x(0:x))$tail x++[0]. Kode ini melakukan operator pembanding (!)(lihat di bawah) pada setiap sel dengan tetangga sebelah kiri pertama, dan kemudian dengan tetangga kanannya, dengan zip daftar xdengan daftar bergeser kanan dan bergeser daftar 0:xkiri tail x++[0]pada gilirannya. Kami menggunakan nol untuk membuat daftar bergeser, karena mereka tidak pernah dapat terjadi dalam matriks yang telah diproses.
a!bdidefinisikan pada baris di atas ini sebagai a!b=div(max(a*a)(a*b))a. Yang ingin kami lakukan di sini adalah perbedaan kasus berikut:
- Jika
sgn(a) = -sgn(b), kami memiliki dua bidang yang berlawanan dalam matriks dan tidak ingin menyatukannya, jadi atetaplah tidak berubah
- Jika
sgn(b) = 0, kami memiliki kasing sudut di mana bbantalan dan oleh karena itu atetap tidak berubah
- Jika
sgn(a) = sgn(b), kami ingin menyatukan dua area dan mengambil satu dengan nilai absolut yang lebih besar (demi kenyamanan).
Catatan yang sgn(a)tidak pernah bisa 0. Kami mencapai ini dengan formula yang diberikan. Jika tanda-tanda adan bberbeda, a*bkurang atau sama dengan nol, sementara a*aselalu lebih besar dari nol, maka kami memilihnya sebagai maksimum dan membaginya dengan auntuk kembali a. Jika tidak, max(a*a)(a*b)adalah abs(a)*max(abs(a),(abs(b)), dan dengan membagi ini dengan a, kita dapatkan sgn(a)*max(abs(a),abs(b)), yang merupakan angka dengan nilai absolut yang lebih besar.
Untuk beralih fungsi ((.)>>=id$transpose.map l)sampai mencapai titik tetap, kami menggunakan (until=<<((==)=<<)), yang diambil dari jawaban stackoverflow ini .
Pengolahan pasca
Untuk postprocessing, kami menggunakan bagian itu
(\x->length.($x).filter<$>[(>0),(<0)]).nub.(>>=id)
yang hanya kumpulan langkah-langkah.
(>>=id)remas daftar daftar menjadi satu daftar,
nubsingkirkan ganda, pisahkan
(\x->length.($x).filter<$>[(>0),(<0)])daftar menjadi sepasang daftar, satu untuk bilangan positif dan satu untuk bilangan negatif, dan hitung panjangnya.
[[1,0];[0,1]]memastikan konektivitas diagonal tidak termasuk