CF 1028A - Find Square

We are given a grid of size $n times m$ consisting of two types of cells, white and black. Initially the entire grid is white, but at some point a single square region with an odd side length was painted black. That square is axis-aligned and fully filled with black cells.

CF 1028A - Find Square

Rating: 800
Tags: implementation
Solve time: 1m 44s
Verified: yes

Solution

Problem Understanding

We are given a grid of size $n \times m$ consisting of two types of cells, white and black. Initially the entire grid is white, but at some point a single square region with an odd side length was painted black. That square is axis-aligned and fully filled with black cells. Outside that square, everything remains white.

The task is to determine the exact center cell of the black square. Because the side length is guaranteed to be odd, there is a unique center cell, and it lies exactly at equal distance from all four sides of the square.

The input is simply the grid after painting. The output is the coordinates of the center cell of the black region.

The constraints are small: $n, m \le 115$. This immediately tells us that even a quadratic scan of the grid is trivial in terms of complexity. Any solution that inspects every cell multiple times will still run comfortably within limits. The structure of the problem is also very rigid, since all black cells belong to one contiguous axis-aligned square with no noise or additional shapes.

The main subtlety is that we are not given the square directly, only its filled pixels. A naive interpretation might try to reason about geometry or bounding shapes incorrectly if we do not carefully extract the exact extent of the black region.

A few edge cases matter:

A minimal square of size $1 \times 1$ means the grid contains exactly one black cell. Any algorithm relying on detecting “area” or “multiple rows” must still handle this correctly. For example:

Input:

1 1
B

Output:

1 1

Another case is when the square touches borders of the grid. The bounding box of black cells may start at row 1 or column 1, so any off-by-one error in indexing will immediately break correctness.

Finally, because the square is guaranteed to be perfect, we must not assume multiple disconnected black components. Any solution that tries to merge regions or perform flood fill is unnecessary but still valid under constraints.

Approaches

A direct brute-force strategy is to scan every possible sub-square in the grid, check whether it is fully black, and whether it forms the unique painted region. For each candidate square, we would validate all cells inside it, which costs $O(k^2)$ per candidate, leading to an overall $O(n^4)$ approach in the worst case. Even though $n \le 115$ makes this borderline feasible in optimized C++, it is unnecessarily complex and slow in Python.

A more natural observation comes from the structure of the input. Every black cell belongs to one axis-aligned square, so all black cells form a contiguous rectangular region where height equals width. Instead of searching for the square directly, we can find the bounding box of all black cells. The topmost, bottommost, leftmost, and rightmost black cells define a rectangle. Since the shape is guaranteed to be a square with odd side length, this bounding rectangle is already the exact square we want.

Once we know the corners of the square, the center is simply the midpoint of the vertical and horizontal ranges.

The brute-force method wastes effort checking correctness of structure we are already guaranteed. The bounding box approach reduces the problem to a single scan of the grid.

Approach Time Complexity Space Complexity Verdict
Brute Force $O(n^2 m^2)$ $O(1)$ Too slow
Optimal (bounding box scan) $O(nm)$ $O(1)$ Accepted

Algorithm Walkthrough

  1. Initialize variables to track the extreme boundaries of black cells: top as a very large value, bottom as very small, similarly for left and right. This ensures that any black cell will correctly update these bounds.
  2. Scan every cell in the grid row by row. Whenever a black cell is encountered, update the four boundary values using its coordinates. This step is essential because the square may appear anywhere in the grid.
  3. After completing the scan, the black square is fully enclosed by the rectangle defined by these boundaries.
  4. Compute the center row as the average of the top and bottom boundaries, and the center column as the average of the left and right boundaries. Because the square side length is guaranteed to be odd, these averages are integers.
  5. Output the computed center coordinates.

Why it works

All black cells belong to a single filled square. The extreme black cells along each direction must correspond exactly to the edges of that square, because any extension beyond them would contradict the definition of a solid filled region. Since the shape is axis-aligned and fully filled, the minimal bounding rectangle of all black cells coincides exactly with the original square. The midpoint of that rectangle is therefore the geometric center of the square, and uniqueness follows from the odd side length.

Python Solution

import sys
input = sys.stdin.readline

def solve():
    n, m = map(int, input().split())
    
    top, bottom = n, -1
    left, right = m, -1
    
    for i in range(n):
        row = input().strip()
        for j, ch in enumerate(row):
            if ch == 'B':
                if i < top:
                    top = i
                if i > bottom:
                    bottom = i
                if j < left:
                    left = j
                if j > right:
                    right = j
    
    # convert to 1-based indexing
    r = (top + bottom) // 2 + 1
    c = (left + right) // 2 + 1
    
    print(r, c)

if __name__ == "__main__":
    solve()

The implementation keeps four running extremes while scanning the grid once. The important detail is that the grid is read using 0-based indices internally, so conversion to 1-based indexing is done only at the final step. The midpoint computation uses integer division, which is safe because the square size is guaranteed to be odd.

Worked Examples

Example 1

Input:

5 6
WWBBBW
WWBBBW
WWBBBW
WWWWWW
WWWWWW

We track boundaries as we scan:

Step Cell top bottom left right
start - 5 -1 6 -1
find B (0,2) 0 0 2 2
find B (0,4) 0 0 2 4
find B (1,2) 0 1 2 4
find B (1,4) 0 1 2 4
find B (2,2) 0 2 2 4
find B (2,4) 0 2 2 4

Final bounds define a rectangle from rows 0 to 2 and columns 2 to 4. The center is:

row = (0 + 2) / 2 + 1 = 2, column = (2 + 4) / 2 + 1 = 4.

Output:

2 4

This confirms that the bounding rectangle exactly captures the square and midpoint computation retrieves the center.

Example 2

Input:

3 3
WWW
WBW
WWW

Only one black cell exists.

Step Cell top bottom left right
start - 3 -1 3 -1
find B (1,1) 1 1 1 1

Center is:

row = 2, column = 2.

Output:

2 2

This shows that even the degenerate 1x1 square case is handled naturally without special branching.

Complexity Analysis

Measure Complexity Explanation
Time $O(nm)$ Every grid cell is scanned exactly once
Space $O(1)$ Only four boundary variables are maintained

The constraints $n, m \le 115$ make a linear scan extremely fast. The solution performs at most about 13,000 cell checks, which is negligible in Python under a 1-second limit.

Test Cases

import sys, io

def run(inp: str) -> str:
    sys.stdin = io.StringIO(inp)
    from collections import deque
    n, m = map(int, input().split())
    top, bottom = n, -1
    left, right = m, -1
    
    for i in range(n):
        row = input().strip()
        for j, ch in enumerate(row):
            if ch == 'B':
                top = min(top, i)
                bottom = max(bottom, i)
                left = min(left, j)
                right = max(right, j)
    
    r = (top + bottom) // 2 + 1
    c = (left + right) // 2 + 1
    return f"{r} {c}"

# provided sample
assert run("5 6\nWWBBBW\nWWBBBW\nWWBBBW\nWWWWWW\nWWWWWW\n") == "2 4"

# single cell square
assert run("1 1\nB\n") == "1 1"

# centered small square
assert run("3 3\nBBB\nBBB\nBBB\n") == "2 2"

# square touching top-left corner
assert run("3 3\nBBW\nBBW\nWWW\n") == "1 1"

# vertical offset square
assert run("4 4\nWWWW\nWBBW\nWBBW\nWWWW\n") == "2 3"
Test input Expected output What it validates
1x1 B 1 1 minimal case
full 3x3 B block 2 2 centered symmetry
top-left aligned square 1 1 boundary correctness
shifted 2x2 square 2 3 general bounding box correctness

Edge Cases

A minimal grid containing a single black cell demonstrates that the algorithm does not require special handling for degenerate squares. The scan sets all boundaries to that single coordinate, and the midpoint returns the same cell.

A square touching the grid border shows that initialization of boundaries must start outside valid indices. If we started from zero instead of extreme sentinels, we could incorrectly shrink the detected region.

A large square filling most of the grid tests whether the scan correctly aggregates multiple updates without overwriting earlier extremes. Every black cell contributes to boundary tightening or expansion, and only the global extremes matter in the final result.