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 1
dan 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 1
wilayah 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 z
sebagai singkatan untuk zipWith
mencukur 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 l
fungsi perbandingan (dirinci di bawah) dan transpose.map l
melakukan setengah dari langkah perbandingan dan transposisi. (.)>>=id
melakukan argumennya dua kali, menjadi bentuk pointfree \f -> f.f
dan lebih pendek satu byte dalam hal ini karena aturan prioritas operator.
l
didefinisikan 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 x
dengan daftar bergeser kanan dan bergeser daftar 0:x
kiri tail x++[0]
pada gilirannya. Kami menggunakan nol untuk membuat daftar bergeser, karena mereka tidak pernah dapat terjadi dalam matriks yang telah diproses.
a!b
didefinisikan 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 a
tetaplah tidak berubah
- Jika
sgn(b) = 0
, kami memiliki kasing sudut di mana b
bantalan dan oleh karena itu a
tetap 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 a
dan b
berbeda, a*b
kurang atau sama dengan nol, sementara a*a
selalu lebih besar dari nol, maka kami memilihnya sebagai maksimum dan membaginya dengan a
untuk 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,
nub
singkirkan 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