D – Grid and Magnet

D – Grid and Magnetはこんな問題です。

D – Grid and Magnet

H 行 W 列のマス目があり、いくつか(0 個のこともある)のマスには磁石が置かれています。
マス目の状態は H 個の 長さ W の文字列 S_1, S_2, …, S_H で表され、 S_i の j 文字目が # のとき上から
i 行目かつ左から j 列目のマスには磁石が置かれていることを、 . のとき何も置かれていないことを表しています。

高橋君は鉄の鎧を着ており、あるマスにいるとき次のように移動することができます。

現在いるマスの上下左右に隣り合うマスのいずれかに磁石が置かれているとき、どこへも移動することができない。
そうでないとき、上下左右に隣り合うマスのいずれかを選んでそのマスに移動することができる。
ただし、マス目の外に移動することはできない。

磁石が置かれていない各マスについて、そのマスの自由度を、「最初高橋くんがそのマスにいるとき、そこから移動を繰り返して到達できるマスの個数」として定義します。 マス目のうち磁石が置かれていないマスの中における、マスの自由度の最大値を求めてください。

ただし、自由度の定義において、「移動を繰り返して到達できるマス」とは、最初にいるマスからそのマスまで移動を繰り返して到達する方法(1 回も移動しないものも含む)が 1 つ以上存在するようなマスのことであり、 最初のマスから始めてすべてのそのようなマスを巡るような移動方法が存在する必要はありません。特に(磁石の置かれていない)各マス自身は、そのマスから「移動を繰り返して到達できるマス」につねに含まれることに注意してください。

入力されるデータ
H W
S_1
S_2

S_H
(ただし、1 ≦ H, W ≦ 1000
S_i は . と # のみからなる長さ W の文字列 )

島探しに似た問題ですが、# の隣を数えるときに注意が必要です。図のように # の隣は複数回数えなければならないのです。

単純に2回カウントしない方法では解が得られないことと、同じ部分を何度もチェックしているとTLE(Time Limit Exceeded – 要は「時間切れ」) してしまうので、これをうまく回避するのがこの問題の肝であるように思われます。

探索の始点をずらすだけでは同じ領域を何度も探索してしまわないようにするのはどうすればよいでしょうか? マス A から マス B へ移動でき、マス B に隣接したマスに磁石が置かれていない時、マス A とマス B の自由度は等しいことはわかります。そこで最初に同じ領域を何度も探索しないように探索の始点になる部分だけを取得することで二回チェックをする部分を減らせないかと考えました。これでTLE を回避しようとしているのですが、残念ながら TLE してしまいます。

工夫したがTLEしてしまうコード

改善策

上記のコードで TLE するのであればどうすればよいのでしょうか? 探索しなくてよい部分の処理を省略することで時間短縮をする以外ありません。問われているのは行動範囲の最大値です。なので最初の BFS メソッドのなかでだいたいの数(磁石の隣を除く領域の広さ)をカウントします。磁石の隣にも移動できることを考慮する場合、考慮しない場合の2倍に2を加えた数が上限となります。

なので磁石の隣を除く領域の広さを取得しておき、ソートします。磁石の隣にも移動できる部分を探索する処理をする前に明らかに解の候補にならないものは弾きます。そうすることで制限時間内に間にあうようになります。