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
ます。ここでは、行に整数のインデックスを、列に奇数の整数のインデックスを使用して2次元配列を圧縮します。これは(i,j)
、式を使用して整数のペアから一意の整数を作成できるため(2^i)*(2j+1)
です。に対して奇数の整数のみを生成する場合j
、計算をスキップできます2*j+1
3バイトを節約ます。
一意の番号を使用すると、行列の値に基づいて符号を乗算するだけで済み、次のように取得されます 2*x-1
反復
繰り返しは
(until=<<((==)=<<))((.)>>=id$transpose.map l)
入力はリストの形式であるため、各行で近隣比較を実行し、行列を転置し、各行で比較を再実行します(転置は以前の列であったものです)、再び転置します。これらの手順のいずれかを実行するコードは
((.)>>=id$transpose.map l)
where l
は比較関数(詳細は後述)でありtranspose.map l
、比較および転置ステップの半分を実行します。(.)>>=id
引数を2回実行します\f -> f.f
。この場合、演算子の優先順位規則により、ポイントフリー形式で1バイト短くなります。
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)
、マトリックスに2つの対向する領域があり、それらを統合したくないため、a
変更されないままです。
- の場合
sgn(b) = 0
、b
パディングであるため、a
変更されないコーナーケースがあります
- の場合
sgn(a) = sgn(b)
、2つの領域を統合し、絶対値が大きい方を使用します(便宜上)。
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 answerから取得したを使用します。
後処理
後処理には、パーツを使用します
(\x->length.($x).filter<$>[(>0),(<0)]).nub.(>>=id)
これは単なる手順の集まりです。
(>>=id)
リストのリストを単一のリストに
nub
押しつぶし、ダブルを取り除き
(\x->length.($x).filter<$>[(>0),(<0)])
、リストを正の数と負の数のリストのペアに分割し、それらの長さを計算します。
[[1,0];[0,1]]
対角線接続が含まれていないことを確認するようなテストケースを追加する必要があります