Haskell , 228 227 225 224 байта
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..]
Попробуйте онлайн!
Объяснение:
Идея этого решения заключается в следующем: инициализировать матрицу с уникальными значениями в каждой ячейке, положительными для 1
и отрицательными для 0
. Затем несколько раз сравнивайте каждую ячейку с ее соседями и, если сосед имеет тот же знак, но число с большим абсолютным значением, замените номер ячейки на номер соседа. Как только это достигнет фиксированной точки, подсчитайте количество различных положительных чисел для количества 1
регионов и различных отрицательных чисел для количества 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..]
можно разделить на предварительную обработку (присвоение номеров ячейкам), итерацию и постобработку (подсчет ячеек)
предварительная обработка
Часть предварительной обработки является функцией
z(\i->z(\j x->2^i*j*(2*x-1))[1,3..])[1..]
Который использует z
как сокращение для того, zipWith
чтобы сбрить несколько байтов. То, что мы здесь делаем, это сжатие двумерного массива с целочисленными индексами в строках и нечетными целочисленными индексами в столбцах. Мы делаем это, так как мы можем построить уникальное целое число из пары целых чисел, (i,j)
используя формулу (2^i)*(2j+1)
. Если мы генерируем только нечетные целые числа для j
, мы можем пропустить вычисление 2*j+1
, сохранив три байта.
С уникальным числом нам теперь нужно только умножить на знак, основанный на значении в матрице, которое получается как 2*x-1
итерация
Итерация выполняется
(until=<<((==)=<<))((.)>>=id$transpose.map l)
Поскольку входные данные представлены в виде списка списков, мы выполняем сравнение соседей в каждой строке, транспонируем матрицу, снова выполняем сравнение в каждой строке (что из-за транспонирования является тем, что было в столбцах ранее) и снова транспонируем. Код, который выполняет один из этих шагов:
((.)>>=id$transpose.map l)
где l
- функция сравнения (подробно описано ниже), которая transpose.map l
выполняет половину шагов сравнения и транспонирования. (.)>>=id
выполняет свой аргумент дважды, будучи бессмысленной формой \f -> f.f
и на один байт короче в этом случае из-за правил приоритета оператора.
l
определяется в строке выше как l x=z(!)(z(!)x(0:x))$tail x++[0]
. Этот код выполняет оператор сравнения (!)
(см. Ниже) для каждой ячейки, в которой сначала находится ее левый сосед, а затем - его правый сосед, путем сжатия списка по очереди x
со сдвинутым вправо списком 0:x
и левым сдвинутым списком tail x++[0]
. Мы используем нули для заполнения сдвинутых списков, поскольку они никогда не могут появляться в предварительно обработанной матрице.
a!b
определяется в строке выше этого как a!b=div(max(a*a)(a*b))a
. То, что мы хотим сделать здесь, это следующее различие в регистре:
- Если
sgn(a) = -sgn(b)
у нас есть две противоположные области в матрице и мы не хотим их объединять, то есть a
остается неизменным
- Если
sgn(b) = 0
у нас есть угловой случай, где b
находится отступ, и, следовательно, a
остается неизменным
- Если
sgn(a) = sgn(b)
мы хотим объединить две области и взять одну с большим абсолютным значением (для удобства).
Обратите внимание, что sgn(a)
никогда не может быть 0
. Мы осуществляем это с помощью приведенной формулы. Если знаки a
и b
отличаются, a*b
меньше или равно нулю, а a*a
всегда больше нуля, поэтому мы выбираем его как максимум и делим a
на, чтобы вернуться a
. Иначе, max(a*a)(a*b)
есть abs(a)*max(abs(a),(abs(b))
, и, деля это на a
, мы получаем sgn(a)*max(abs(a),abs(b))
, который является числом с большим абсолютным значением.
Для итерации функции, ((.)>>=id$transpose.map l)
пока она не достигнет фиксированной точки, мы используем (until=<<((==)=<<))
, что взято из этого ответа stackoverflow .
Постобработка
Для постобработки мы используем часть
(\x->length.($x).filter<$>[(>0),(<0)]).nub.(>>=id)
которая является просто набором шагов.
(>>=id)
объединяет список списков в один список,
nub
избавляется от двойников,
(\x->length.($x).filter<$>[(>0),(<0)])
разбивает список на пару списков, один для положительных и один для отрицательных чисел, и вычисляет их длины.
[[1,0];[0,1]]