Max Sum of Rectangle No Larger Than K stacks two classic tricks on top of each other: collapsing a 2-D problem into a 1-D one by fixing a pair of columns, and finding the largest subarray sum that stays under a cap with a running prefix and an ordered set.
Problem. Given an m x n matrix and an integer k, return the maximum sum of any rectangle in the
matrix whose sum is no larger than k. It is guaranteed at least one such rectangle exists.
Example: matrix = [[1, 0, 1], [0, -2, 3]], k = 2 → answer 2 (the single cell 2 you get from
columns 0..2 of row 0, or equivalently the rectangle summing to exactly k).
The slow way first
The brute force enumerates every rectangle by its four edges — top row, bottom row, left column, right column — and sums it. That is four nested loops to pick the box plus more work to add it up: roughly O(m² n² · mn). Hopeless for anything but tiny grids.
The first cut: do not re-pick all four edges independently. If we fix the left and right columns, every rectangle between them is just a contiguous band of rows. So we can collapse those columns into a single 1-D array of row sums and ask a much smaller question about that array.
The idea: collapse to 1-D, then cap the subarray sum
For a fixed column pair we now have a 1-D array row_sums. We want its largest subarray sum that is <= k. Keep a running prefix and an ordered set of every earlier prefix. A subarray ending here has sum prefix - earlier_prefix; to make that <= k and as large as possible, we want the smallest earlier prefix that is >= prefix - k. An ordered set finds it with a binary search in O(log m).
Walk through it
Step through the animation. The L and R markers pick a column pair; the row-sum array updates as R slides right (we just add the new column, never recompute). For each pair we run the 1-D scan and update best. With k = 2, columns 0..2 give row sums [2, 1], whose best capped subarray is 2 — exactly k, so we cannot do better.
Pseudocode
best = -infinity
for left in all columns:
row_sums = [0 for each row]
for right from left to last column:
add column "right" into every row_sums[r]
# now find the largest subarray sum of row_sums that is <= k
seen = ordered set containing {0}
prefix = 0
for s in row_sums:
prefix += s
target = prefix - k
earlier = smallest value in seen that is >= target
if earlier exists:
best = max(best, prefix - earlier)
add prefix to seen
return bestThe Python solution
def max_sum_rect(matrix, k):
cols = len(matrix[0])
best = float("-inf")
for left in range(cols):
row_sums = [0] * len(matrix)
for right in range(left, cols):
for r in range(len(matrix)):
row_sums[r] += matrix[r][right]
seen = SortedList([0])
prefix = 0
for s in row_sums:
prefix += s
j = seen.bisect_left(prefix - k)
if j < len(seen):
best = max(best, prefix - seen[j])
seen.add(prefix)
return bestleftpins the left edge;row_sumsstarts fresh for each newleft.- As
rightslides outward we only add columnrightinto each row sum, so building the band is cheap. seenis an ordered set (fromsortedcontainers) seeded with0— the empty prefix, which represents a subarray that starts at the very beginning.prefix - kis the target: we want the smallest earlier prefix that is>= target, so the gapprefix - earlierlands<= k.bisect_leftfinds the insertion point forprefix - k;seen[j]is that smallest qualifying earlier prefix, andprefix - seen[j]is the best capped subarray ending here.
Complexity
| Case | Time | Notes |
|---|---|---|
| Brute force (all rectangles) | O(m^2 n^2) (moderate) | pick four edges, sum each |
| Fix columns + ordered set | O(n^2 m log m) (moderate) | n^2 column pairs, O(m log m) scan |
O(m) (moderate)Pick the smaller dimension as the inner one: loop columns over the wider side so the ordered-set scan runs over the shorter side. The space is O(m) for the row sums and the ordered set.
When this pattern shows up
Two reusable moves live here. First: fix two columns (or two rows) to turn a 2-D matrix problem into a 1-D array problem — the same collapse powers "maximum sum rectangle" and many submatrix questions. Second: prefix sums + an ordered set answer "largest/smallest subarray sum under a bound" whenever a plain sliding window fails because the array has negative numbers.
A sliding window does not work once values can be negative — growing the window can lower the sum, so the monotonic shrink/grow logic breaks. That is exactly why we fall back to prefix sums plus an ordered set: it handles negatives by searching all earlier prefixes, not just a window edge.
Practice
For row sums [2, 1] and k = 2, with seen = {0} then {0, 2}, what is the best subarray sum that stays <= k?
1. Why do we fix a pair of columns first?
2. Why use an ordered set of prefixes instead of a sliding window?
3. For a running prefix P, which earlier prefix gives the largest subarray sum that is <= k?
4. What is the overall time complexity (m rows, n columns)?