3548. Equal Sum Grid Partition II
3548. Equal Sum Grid Partition II
What are we solving?
You have a 2D grid of positive integers. Make exactly one straight cut — either a full horizontal line (between two rows) or a full vertical line (between two columns) — splitting the grid into exactly two non-empty halves.
Goal: Can you make the two halves have equal sums? You are allowed to optionally remove one cell from one of the halves first — but only if the remaining cells stay connected (4-directionally).
Return true if a valid cut exists, false otherwise.
For any horizontal cut after row i, compute topSum and botSum. Their difference is diff = topSum − botSum. To balance the two halves you need to remove a cell whose value equals |diff| from the heavier side.
canPartition() function on 4 variants of the grid — original, reversed, transposed, and reversed+transposed — to cover all horizontal and vertical cut directions including both boundary sides.
-
1Compute total sum of entire grid once. O(m×n).
-
2Run canPartition() on 4 orientations:
① Original → H-cuts top→bottom
② Reversed rows → H-cuts bottom→top
③ Transposed → V-cuts left→right
④ Reversed+Transposed → V-cuts right→left -
3Inside canPartition: Sweep row by row, accumulating
topSum. Computediff = topSum − botSumat each position. -
4Four early-return checks:
diff == 0→ already balanced
diff == grid[0][0]→ remove top-left corner
diff == grid[0][last]→ remove top-right corner
diff == grid[i][0]→ remove cut-edge first cell -
5HashSet seen accumulates all cell values from rows scanned so far. If
diffis inseen(and constraints hold), returntrue. ⚠ Must guard against integer overflow here!
import java.util.*; class Solution { public boolean canPartitionGrid(int[][] grid) { final long sum = Arrays.stream(grid) .flatMapToInt(Arrays::stream) .asLongStream().sum(); return canPartition(grid, sum) || canPartition(reversed(grid), sum) || canPartition(transposed(grid), sum) || canPartition(reversed(transposed(grid)), sum); } private boolean canPartition(int[][] grid, long sum) { long topSum = 0; Set<Integer> seen = new HashSet<>(); for (int i = 0; i < grid.length; ++i) { topSum += Arrays.stream(grid[i]).asLongStream().sum(); final long botSum = sum - topSum; final long diff = topSum - botSum; Arrays.stream(grid[i]).forEach(seen::add); // Case 1: already balanced if (diff == 0) return true; // Cases 2-4: remove a corner / cut-edge cell if (diff == grid[0][0]) return true; if (diff == grid[0][grid[0].length - 1]) return true; if (diff == grid[i][0]) return true; // Case 5: any seen cell matches diff // ✅ Guard: cell values ∈ [1,100000] — prevents int overflow cast if (grid[0].length > 1 && i > 0 && diff >= 1 && diff <= 100_000 && seen.contains((int) diff)) return true; } return false; } // Transpose: rows become columns private int[][] transposed(int[][] grid) { int[][] res = new int[grid[0].length][grid.length]; for (int i = 0; i < grid.length; i++) for (int j = 0; j < grid[0].length; j++) res[j][i] = grid[i][j]; return res; } // Reverse row order private int[][] reversed(int[][] grid) { return Arrays.stream(grid) .collect(java.util.stream.Collectors.collectingAndThen( java.util.stream.Collectors.toList(), list -> { Collections.reverse(list); return list.toArray(new int[0][]); })); } }
⚠ Root Cause: (int) diff silently overflows
The original walkccc reference solution writes:
seen.contains((int)(topSum - botSum))
Here diff = topSum − botSum is a long. On large grids (thousands of rows × 100,000 values), diff can exceed Integer.MAX_VALUE (~2.1 billion). Casting such a value to int silently wraps around in Java — producing a garbage integer that may accidentally equal a cell value in seen, triggering a false positive.
[100000, 100000] has total = 10¹⁰. Near the last row, diff ≈ 10¹⁰ > 2³¹. Casting: (int)(10_000_000_000L) = 1_410_065_408 — which could accidentally match a value in seen and incorrectly return true.
The test case you provided has exactly this structure: a very large 2-column grid with thousands of rows of [100000, 100000], causing diff to grow beyond Integer.MAX_VALUE at later cut positions.
✓ Guard with a value-range check
Since all grid cell values are bounded to [1, 100_000], diff can only ever legitimately match a cell value when it falls in that range. Add this guard before casting:
// ❌ WRONG — (int) diff overflows when diff > Integer.MAX_VALUE if (grid[0].length > 1 && i > 0 && seen.contains((int) (topSum - botSum))) return true;
// ✅ CORRECT — range guard prevents overflow cast final long diff = topSum - botSum; if (grid[0].length > 1 && i > 0 && diff >= 1 && diff <= 100_000 // ← the fix && seen.contains((int) diff)) return true;
diff >= 1 && diff <= 100_000 short-circuits before the cast whenever diff is out of the valid cell-value range. The (int) cast is now only reached when diff is guaranteed to fit safely.
Prefix Sum + HashSet (4 sweeps)
Sweep in all 4 directions via transformation. Track seen values, check diff.
Check all cuts × all cells
For each cut, try removing every border cell with BFS connectivity check.
Prefix Sum + Value Map
Precompute row/col sums. For each cut, look up diff in a value→position map and verify boundary.
Segment Tree on Rows/Cols
Build a segment tree for fast range sum queries. Useful for dynamic updates, not this problem.
| Approach | Time | Space | Recommended |
|---|---|---|---|
| Prefix Sum + HashSet (fixed) | O(mn) | O(mn) | ✓ Yes |
| Prefix Sum + HashSet (original) | O(mn) | O(mn) | ✗ Overflow bug |
| Value Map approach | O(mn) | O(mn) | ~ Viable |
| Brute Force | O(m²n²) | O(mn) | ✗ TLE |