Day 085-Q2 — 最大部分矩形和(2D Kadane・$O(H^2 W)$)

2026-07-08 赤色 Master / Phase 8+ ★★★★★★★★★ 2D Maximum Subarray・行ペア固定・縦和差分

問題

$H \times W$ の整数行列 $A$(負値を含む)が与えられる。行列内の空でない部分矩形(連続行区間 $\times$ 連続列区間)の要素和のうち、最大値を求めよ。

制約

パラメータ範囲備考
$H, W$$\le 500$行数・列数
$A_{i,j}$$-10^9 \le A_{i,j} \le 10^9$負値あり
選択$\ge 1$ 要素空矩形不可

入出力例

入力例1

4 4
1 -2 3 4
-5 6 -1 2
2 -3 4 -1
1 2 -6 3

出力例1

12

概念図: 行ペア固定 → 縦和列 → 1D Kadane

行区間 [t,b] を固定 → 各列の縦和 → 1D 最大部分和 1 -2 3 4 -5 6 -1 2 ... row 2 ... ... row 3 ... ↓ 縦和 col[j] = A[t..b][j] を差分更新(b を増やすたび O(W)) -4 4 2 6 ← 縦和列(1次元) 1D Kadane(空を許さない) cur = c if cur < 0 else cur + c col = [-4, 4, 2, 6] j=0: cur=-4 best=-4 j=1: cur=4 best=4 j=2: cur=6 best=6 j=3: cur=12 best=12 ✓ → この行ペアの最大 = 12

ヒント

ヒント1(方向性)

1次元の最大部分配列和は Kadane 法で $O(n)$。2次元では「上端 $t$・下端 $b$ を固定」し、各列で行 $[t,b]$ を縦に潰した1次元配列に Kadane を適用する。

ヒント2(アプローチ)

$H^2$ 通りの行ペアそれぞれで $O(W)$ の Kadane。縦和を差分更新すれば全体 $O(H^2 W)$。$500^2 \times 500 = 1.25\times10^8$ で間に合う。

ヒント3(ほぼ答え)
for t in range(H):
    col = [0]*W
    for b in range(t, H):
        for j in range(W):
            col[j] += A[b][j]   # 下端行を差分追加
        cur = best = col[0]     # 1D Kadane(空不可)
        for j in range(1, W):
            cur = col[j] if cur < 0 else cur + col[j]
            best = max(best, cur)
        ans = max(ans, best)

模範解答

import sys
input = sys.stdin.readline

def solve():
    H, W = map(int, input().split())
    A = [list(map(int, input().split())) for _ in range(H)]

    ans = float('-inf')
    for t in range(H):
        col = [0] * W
        for b in range(t, H):
            rowB = A[b]
            for j in range(W):
                col[j] += rowB[j]
            cur = col[0]
            best = col[0]
            for j in range(1, W):
                cj = col[j]
                cur = cj if cur < 0 else cur + cj
                if cur > best:
                    best = cur
            if best > ans:
                ans = best
    print(ans)

solve()

計算量: $O(H^2 W)$ 時間、$O(W)$ 追加空間。

Step-by-Step 解説

Step 1: 問題の分解

部分矩形 = 行区間 $[t,b]$ × 列区間 $[l,r]$。行区間を全探索すれば、残りは各列縦和からなる1次元列の最大部分和に帰着。

Step 2: 縦和の差分更新

操作コスト備考
上端 $t$ 固定・下端 $b$ 増加$O(W)$/回col[j] += A[b][j]
行ペア総数$O(H^2)$$t \le b$

Step 3: Kadane(空を許さない版)

cur = c if cur < 0 else cur + c は「これまでの和が負なら捨てて単独再スタート」。初期値を配列先頭にするため負値のみでも最大単一要素を返す。

Step 4: 計算量

$O(H^2 W) = 500^2 \times 500 = 1.25\times10^8$。内側ループを最小演算に絞れば通る(CPython なら numpy 化も有効)。

よくあるミス

ミス原因正しい書き方
空矩形を許して 0 を返すKadane 初期値を 0初期値 col[0](最低1要素)
縦和を毎回再計算し TLE差分更新なしcol[j] += A[b][j]
int64 オーバーフロー(他言語)和が $5\times10^{14}$ 級Python は多倍長で安全

次のステップ

  • 発展問題: 面積制約付き最大和矩形(辺長 $\ge k$ 制約)
  • 発展問題: 最大和矩形を numpy / prefix sum で定数倍高速化

自己評価

理解度: / /

自分の回答:

気づき・メモ: