LeetCode 3025 - Find the Number of Ways to Place People I
The problem gives us a list of distinct points on a 2D plane. Each point is represented as [x, y], where x is the horizontal coordinate and y is the vertical coordinate. We must count how many ordered pairs (A, B) satisfy two conditions: 1.
Difficulty: 🟡 Medium
Topics: Array, Math, Geometry, Sorting, Enumeration
Solution
Problem Understanding
The problem gives us a list of distinct points on a 2D plane. Each point is represented as [x, y], where x is the horizontal coordinate and y is the vertical coordinate.
We must count how many ordered pairs (A, B) satisfy two conditions:
- Point
Ais on the upper-left side of pointB - The rectangle formed by
AandB, including its border, contains no other points exceptAandB
To understand the geometry precisely, suppose:
A = (x1, y1)B = (x2, y2)
For A to be on the upper-left side of B:
x1 <= x2y1 >= y2
This means A lies leftward and upward relative to B.
The rectangle formed by these two points includes all coordinates:
x1 <= x <= x2y2 <= y <= y1
The pair is valid only if no third point lies anywhere inside or on the boundary of this rectangle.
An important detail is that degenerate rectangles are allowed. If the two points lie on the same horizontal or vertical line, the "rectangle" becomes a line segment, and the same emptiness rule still applies.
The constraints are very small:
2 <= n <= 50- Coordinates are between
0and50 - All points are distinct
Because n is at most 50, even cubic solutions are acceptable. This strongly influences the algorithm choice. We do not need advanced geometry structures or sweep-line optimizations.
Several edge cases are important:
- Multiple points may lie on the same vertical or horizontal line
- A point exactly on the rectangle border invalidates the pair
- Degenerate rectangles still count as valid regions that must be empty
- Since pairs are ordered,
(A, B)and(B, A)are different geometrically, but only one direction can satisfy the upper-left condition
Approaches
Brute Force Approach
The most direct solution is to examine every ordered pair of points (A, B).
For each pair:
- Check whether
Ais upper-left ofB - If it is, scan all other points
- Determine whether any point lies inside or on the boundary of the rectangle formed by
AandB - If no such point exists, increment the answer
This works because the constraints are small enough to allow checking every possible rectangle explicitly.
Suppose there are n points:
- There are
O(n²)candidate pairs - For each pair, we may scan all
npoints
This produces an O(n³) solution.
Even though cubic time is usually large, here n <= 50, so:
50³ = 125,000
which is completely manageable.
Key Insight for the Better Solution
The main observation is that the constraints are intentionally small. There is no need for complicated data structures or spatial indexing.
However, we can still organize the logic cleanly by:
- Iterating through all candidate pairs
- Immediately rejecting invalid geometric orientations
- Using simple rectangle boundary checks for obstruction detection
The "optimal" solution for this problem is still O(n³), but it is carefully structured and efficient enough for the given limits.
The important improvement is conceptual clarity rather than asymptotic reduction. We minimize unnecessary work by only checking rectangles for geometrically valid pairs.
Approach Comparison
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n³) | O(1) | Checks every pair and scans all points |
| Optimal | O(n³) | O(1) | Same asymptotic complexity, but cleaner geometric filtering and efficient validation |
Algorithm Walkthrough
Step 1: Iterate Through Every Ordered Pair
We consider every ordered pair of points (A, B).
For each pair:
A = (x1, y1)B = (x2, y2)
We skip pairs where A and B are the same point.
This guarantees that every possible candidate relationship is examined.
Step 2: Check the Upper-Left Condition
A valid pair must satisfy:
x1 <= x2y1 >= y2
If either condition fails, then A is not positioned upper-left relative to B, so the pair cannot contribute to the answer.
This immediately prunes many invalid pairs.
Step 3: Define the Rectangle Boundaries
For a valid geometric pair, the rectangle boundaries are:
- Left boundary:
x1 - Right boundary:
x2 - Bottom boundary:
y2 - Top boundary:
y1
Every point inside or on the border of this rectangle must be checked.
Step 4: Scan All Other Points
We iterate through all points again.
For each third point (x, y):
- Ignore
A - Ignore
B
Then check whether:
x1 <= x <= x2y2 <= y <= y1
If both are true, the point lies inside or on the border of the rectangle.
In that case, the pair is invalid.
Step 5: Count Valid Pairs
If the scan finishes without finding any blocking point, then the rectangle is empty except for A and B.
We increment the answer.
Step 6: Return the Final Count
After examining all ordered pairs, return the total number of valid pairs.
Why it works
The algorithm directly implements the exact problem definition.
For every ordered pair, it verifies:
- The required geometric orientation
- The absence of all other points within the rectangle boundary
Since every candidate pair is checked exhaustively and every obstructing point is tested explicitly, no valid pair is missed and no invalid pair is counted.
Python Solution
from typing import List
class Solution:
def numberOfPairs(self, points: List[List[int]]) -> int:
n = len(points)
answer = 0
for i in range(n):
x1, y1 = points[i]
for j in range(n):
if i == j:
continue
x2, y2 = points[j]
# A must be upper-left of B
if x1 > x2 or y1 < y2:
continue
valid = True
for k in range(n):
if k == i or k == j:
continue
x, y = points[k]
# Point lies inside or on border
if x1 <= x <= x2 and y2 <= y <= y1:
valid = False
break
if valid:
answer += 1
return answer
The implementation follows the algorithm exactly.
The outer two loops enumerate every ordered pair of points. For each pair, the code first checks whether the geometric upper-left condition is satisfied. If not, the pair is skipped immediately.
Once a geometrically valid pair is found, the third loop scans all remaining points. The rectangle inclusion test:
x1 <= x <= x2 and y2 <= y <= y1
determines whether a point lies inside or on the rectangle boundary.
If any such point exists, the pair becomes invalid. Otherwise, the pair contributes to the answer.
Because the constraints are small, this direct geometric implementation is both simple and efficient.
Go Solution
func numberOfPairs(points [][]int) int {
n := len(points)
answer := 0
for i := 0; i < n; i++ {
x1, y1 := points[i][0], points[i][1]
for j := 0; j < n; j++ {
if i == j {
continue
}
x2, y2 := points[j][0], points[j][1]
// A must be upper-left of B
if x1 > x2 || y1 < y2 {
continue
}
valid := true
for k := 0; k < n; k++ {
if k == i || k == j {
continue
}
x, y := points[k][0], points[k][1]
// Point lies inside or on border
if x1 <= x && x <= x2 &&
y2 <= y && y <= y1 {
valid = false
break
}
}
if valid {
answer++
}
}
}
return answer
}
The Go implementation mirrors the Python logic closely.
Since coordinate values are tiny, standard int arithmetic is fully safe and there is no overflow concern.
Go slices are used naturally for the input points. No additional memory allocation is required beyond a few local variables, so the solution remains constant-space.
Worked Examples
Example 1
Input:
points = [[1,1],[2,2],[3,3]]
Candidate Pairs
| A | B | Upper-left valid? |
|---|---|---|
| (1,1) | (2,2) | No |
| (1,1) | (3,3) | No |
| (2,2) | (1,1) | No |
| (2,2) | (3,3) | No |
| (3,3) | (1,1) | No |
| (3,3) | (2,2) | No |
No pair satisfies:
x1 <= x2y1 >= y2
Therefore:
Answer = 0
Example 2
Input:
points = [[6,2],[4,4],[2,6]]
Label the points:
| Index | Point |
|---|---|
| 0 | (6,2) |
| 1 | (4,4) |
| 2 | (2,6) |
Pair: (4,4) → (6,2)
Rectangle:
4 <= x <= 62 <= y <= 4
Check third point (2,6):
x = 2is outside
No obstruction exists.
Valid pair count becomes 1.
Pair: (2,6) → (4,4)
Rectangle:
2 <= x <= 44 <= y <= 6
Check third point (6,2):
- Outside rectangle
Valid pair count becomes 2.
Pair: (2,6) → (6,2)
Rectangle:
2 <= x <= 62 <= y <= 6
Point (4,4) lies inside.
This pair is invalid.
Final answer:
2
Example 3
Input:
points = [[3,1],[1,3],[1,1]]
Label the points:
| Index | Point |
|---|---|
| 0 | (3,1) |
| 1 | (1,3) |
| 2 | (1,1) |
Pair: (1,1) → (3,1)
Rectangle becomes a horizontal line:
1 <= x <= 3y = 1
No other point lies on this segment.
Valid.
Pair: (1,3) → (1,1)
Rectangle becomes a vertical line:
x = 11 <= y <= 3
No other point lies on this segment.
Valid.
Pair: (1,3) → (3,1)
Rectangle:
1 <= x <= 31 <= y <= 3
Point (1,1) lies on the border.
Invalid.
Final answer:
2
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n³) | Three nested loops over all points |
| Space | O(1) | Only a few variables are used |
The algorithm examines every ordered pair of points, which requires O(n²) work. For each pair, it scans all points again to detect obstructions, adding another factor of n.
Because no auxiliary data structures proportional to input size are used, the extra space complexity remains constant.
Test Cases
from typing import List
class Solution:
def numberOfPairs(self, points: List[List[int]]) -> int:
n = len(points)
answer = 0
for i in range(n):
x1, y1 = points[i]
for j in range(n):
if i == j:
continue
x2, y2 = points[j]
if x1 > x2 or y1 < y2:
continue
valid = True
for k in range(n):
if k == i or k == j:
continue
x, y = points[k]
if x1 <= x <= x2 and y2 <= y <= y1:
valid = False
break
if valid:
answer += 1
return answer
sol = Solution()
assert sol.numberOfPairs([[1,1],[2,2],[3,3]]) == 0 # strictly increasing diagonal
assert sol.numberOfPairs([[6,2],[4,4],[2,6]]) == 2 # example with blocking point
assert sol.numberOfPairs([[3,1],[1,3],[1,1]]) == 2 # border and line cases
assert sol.numberOfPairs([[1,5],[5,1]]) == 1 # simple valid rectangle
assert sol.numberOfPairs([[1,5],[3,3],[5,1]]) == 0 # middle point blocks
assert sol.numberOfPairs([[1,1],[1,2]]) == 0 # vertical but wrong orientation
assert sol.numberOfPairs([[1,2],[1,1]]) == 1 # vertical valid line
assert sol.numberOfPairs([[1,1],[2,1]]) == 1 # horizontal valid line
assert sol.numberOfPairs([[1,1],[2,1],[3,1]]) == 2 # multiple horizontal segments
assert sol.numberOfPairs([[0,50],[50,0]]) == 1 # extreme coordinate values
assert sol.numberOfPairs([[1,4],[2,3],[3,2],[4,1]]) == 3 # chained valid pairs
assert sol.numberOfPairs([[1,5],[2,4],[3,3],[4,2],[5,1]]) == 4 # nested blocking rectangles
Test Summary
| Test | Why |
|---|---|
[[1,1],[2,2],[3,3]] |
No upper-left relationships exist |
[[6,2],[4,4],[2,6]] |
Interior blocking point |
[[3,1],[1,3],[1,1]] |
Border points invalidate rectangles |
[[1,5],[5,1]] |
Simplest valid rectangle |
[[1,5],[3,3],[5,1]] |
Point inside rectangle invalidates pair |
[[1,1],[1,2]] |
Wrong vertical orientation |
[[1,2],[1,1]] |
Degenerate vertical line |
[[1,1],[2,1]] |
Degenerate horizontal line |
[[1,1],[2,1],[3,1]] |
Multiple line-segment checks |
[[0,50],[50,0]] |
Coordinate extremes |
[[1,4],[2,3],[3,2],[4,1]] |
Multiple independent valid pairs |
[[1,5],[2,4],[3,3],[4,2],[5,1]] |
Nested rectangles and blockers |
Edge Cases
One important edge case is when two points form a line rather than a proper rectangle. This happens when the points share the same x coordinate or the same y coordinate. A naive implementation might accidentally ignore these cases because the rectangle has zero width or zero height. However, the problem explicitly allows these degenerate rectangles. The implementation handles this correctly because the boundary checks use inclusive inequalities, so line segments are treated naturally.
Another critical edge case occurs when a third point lies exactly on the border of the rectangle. Many geometry problems distinguish between interior and boundary points, but this problem explicitly states that the border also counts. A pair becomes invalid even if another point touches the rectangle edge. The implementation correctly handles this using inclusive comparisons:
x1 <= x <= x2 and y2 <= y <= y1
This ensures border points invalidate the pair.
A third important case is when multiple points appear in descending diagonal order, such as:
[(1,5), (2,4), (3,3), (4,2)]
Many pairs satisfy the upper-left condition geometrically, but larger rectangles contain intermediate points. A naive solution might count all geometric pairs without verifying emptiness carefully. The implementation avoids this bug by scanning every third point before accepting a pair.
Finally, it is important that points are distinct. The problem guarantees uniqueness, which simplifies the implementation because we never need to handle duplicate coordinates or distinguish between multiple identical points.