A - 最小の額縁 / Smallest Frame Editorial by admin
Claude 4.6 Opus (Thinking)概要
グリッド上にある全ての # タイルを含む最小の長方形を求め、その周の長さを出力する問題です。# が存在しない場合は \(0\) を出力します。
考察
すべての # タイルを含む長方形の周の長さを最小化したいので、長方形をできるだけ小さくする必要があります。
重要な気づき: すべての # を含む最小の長方形は、# が存在する行の最小値・最大値と列の最小値・最大値によって一意に決まります。これはいわゆる バウンディングボックス(bounding box) と呼ばれるものです。
例えば、以下のような \(4 \times 5\) のグリッドを考えます:
.....
.#...
...#.
.....
#が存在する行: 2行目と3行目 → \(r_1 = 2, r_2 = 3\)#が存在する列: 2列目と4列目 → \(c_1 = 2, c_2 = 4\)
この長方形の周の長さは \(2 \times ((3 - 2 + 1) + (4 - 2 + 1)) = 2 \times (2 + 3) = 10\) となります。
これより小さい長方形では、いずれかの # がはみ出してしまうため、この長方形が最小です。
素朴なアプローチとの比較: 全ての \((r_1, r_2, c_1, c_2)\) の組み合わせを試すと \(O(H^2 W^2)\) になりますが、バウンディングボックスは一意に決まるので、全タイルを1回走査するだけで求められます。
アルゴリズム
- グリッドを1マスずつ走査する。
#を見つけるたびに、以下の4つの値を更新する:- \(\text{min\_r}\):
#が存在する行の最小値 - \(\text{max\_r}\):
#が存在する行の最大値 - \(\text{min\_c}\):
#が存在する列の最小値 - \(\text{max\_c}\):
#が存在する列の最大値
- \(\text{min\_r}\):
- 走査終了後、
#が1つも存在しなければ \(0\) を出力する。 - 存在すれば、周の長さ \(2 \times ((\text{max\_r} - \text{min\_r} + 1) + (\text{max\_c} - \text{min\_c} + 1))\) を出力する。
計算量
- 時間計算量: \(O(H \times W)\)(全タイルを1回ずつ走査するだけ)
- 空間計算量: \(O(W)\)(各行の文字列を1行ずつ読み込むだけで済む)
実装のポイント
初期値の設定: \(\text{min\_r}\) と \(\text{min\_c}\) は十分大きい値(\(H+1, W+1\))に、\(\text{max\_r}\) と \(\text{max\_c}\) は \(0\) に初期化しておくことで、最初の
#発見時に正しく更新されます。#が存在しない場合の判定: 走査後に \(\text{max\_r} = 0\) のままであれば、#は1つも見つからなかったと判定できます。1-indexed vs 0-indexed: コード中では行・列を1-indexedで管理していますが、差分を取って周の長さを計算するため、0-indexedでも最終結果は同じになります。
ソースコード
H, W = map(int, input().split())
min_r, max_r, min_c, max_c = H + 1, 0, W + 1, 0
for i in range(H):
s = input()
for j in range(W):
if s[j] == '#':
if i + 1 < min_r:
min_r = i + 1
if i + 1 > max_r:
max_r = i + 1
if j + 1 < min_c:
min_c = j + 1
if j + 1 > max_c:
max_c = j + 1
if max_r == 0:
print(0)
else:
print(2 * ((max_r - min_r + 1) + (max_c - min_c + 1)))
この解説は claude4.6opus-thinking によって生成されました。