Find the Number of Ways to Place People I
MedPrompt
You are given a 2D array points of size n x 2 representing integer coordinates of some points on a 2D plane, where points[i] = [xi, yi].
Count the number of pairs of points (A, B), where
Ais on the upper left side ofB, and- there are no other points in the rectangle (or line) they make (including the border).
Return the count.
Example 1:
Input: points = [[1,1],[2,2],[3,3]]
Output: 0
Explanation:

There is no way to choose A and B so A is on the upper left side of B.
Example 2:
Input: points = [[6,2],[4,4],[2,6]]
Output: 2
Explanation:

- The left one is the pair
(points[1], points[0]), wherepoints[1]is on the upper left side ofpoints[0]and the rectangle is empty. - The middle one is the pair
(points[2], points[1]), same as the left one it is a valid pair. - The right one is the pair
(points[2], points[0]), wherepoints[2]is on the upper left side ofpoints[0], butpoints[1]is inside the rectangle so it's not a valid pair.
Example 3:
Input: points = [[3,1],[1,3],[1,1]]
Output: 2
Explanation:

- The left one is the pair
(points[2], points[0]), wherepoints[2]is on the upper left side ofpoints[0]and there are no other points on the line they form. Note that it is a valid state when the two points form a line. - The middle one is the pair
(points[1], points[2]), it is a valid pair same as the left one. - The right one is the pair
(points[1], points[0]), it is not a valid pair aspoints[2]is on the border of the rectangle.
Constraints:
2 <= n <= 50points[i].length == 20 <= points[i][0], points[i][1] <= 50- All
points[i]are distinct.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach directly translates the problem statement into code. It iterates through all possible pairs of points (A, B) and for each pair, it first checks if A is to the upper-left of B. If this condition is met, it then performs another iteration through all other points (C) to check if any of them lie inside the rectangle formed by A and B. If no such point C is found, the pair (A, B) is counted.
Algorithm
- Initialize a counter
countto 0. - Iterate through each point
Ain thepointsarray using an indexi. - In a nested loop, iterate through each point
Bin thepointsarray using an indexj. - If
iandjare the same, skip to the next iteration to avoid comparing a point with itself. - Check if point
Ais on the upper-left side of pointB. This condition ispoints[i][0] <= points[j][0]andpoints[i][1] >= points[j][1]. - If the upper-left condition is met, we must check if the rectangle they form is empty. Initialize a boolean flag
is_emptytotrue. - Start a third nested loop, iterating through every point
Cwith indexk. - If
kis the same asiorj, skip. - Check if point
Clies inside or on the boundary of the rectangle formed byAandB. The condition ispoints[k][0] >= points[i][0],points[k][0] <= points[j][0],points[k][1] >= points[j][1], andpoints[k][1] <= points[i][1]. - If such a point
Cis found, setis_emptytofalseand break the innermost loop. - After checking all points
C, ifis_emptyis stilltrue, it means we found a valid pair. Incrementcount. - After all loops complete, return the final
count.
Walkthrough
The brute-force algorithm systematically checks every possible ordered pair of points (A, B). For each pair, it first validates the geometric condition: A.x <= B.x and A.y >= B.y. If this condition holds, the algorithm proceeds to check for the 'empty rectangle' constraint. It does this by iterating through every other point C in the list and checking if C lies inside or on the boundary of the rectangle defined by A and B. A point C is inside if A.x <= C.x <= B.x and B.y <= C.y <= A.y. If any such point C is found, the rectangle is not empty, and the pair (A, B) is invalid. If the loop over all points C completes without finding any interfering point, the pair (A, B) is valid, and a counter is incremented. This process is repeated for all pairs to find the total count.
class Solution { public int numberOfPairs(int[][] points) { int n = points.length; int count = 0; for (int i = 0; i < n; i++) { // Point A for (int j = 0; j < n; j++) { // Point B if (i == j) continue; int x1 = points[i][0]; int y1 = points[i][1]; int x2 = points[j][0]; int y2 = points[j][1]; // Check if A is upper-left of B if (x1 <= x2 && y1 >= y2) { boolean isRectangleEmpty = true; // Check for any other point C inside the rectangle for (int k = 0; k < n; k++) { if (k == i || k == j) continue; int x3 = points[k][0]; int y3 = points[k][1]; if (x3 >= x1 && x3 <= x2 && y3 >= y2 && y3 <= y1) { isRectangleEmpty = false; break; } } if (isRectangleEmpty) { count++; } } } } return count; }}Complexity
Time
O(n^3), where `n` is the number of points. There are two nested loops to select a pair of points `(A, B)`, and a third nested loop to check for any interfering point `C`. This results in a cubic number of operations.
Space
O(1) extra space. The algorithm only requires a few variables for loop counters and the result, not dependent on the input size.
Trade-offs
Pros
It is straightforward to understand and implement as it directly follows the problem's definition.
It is guaranteed to be correct and works well for the given small constraints (n <= 50).
Cons
The O(n^3) time complexity makes it inefficient and potentially too slow if the constraints on
nwere larger.
This approach significantly improves efficiency by pre-sorting the points. By sorting the points by their x-coordinates (and y-coordinates as a tie-breaker), we can eliminate the third loop used in the brute-force method. For each point A, we iterate through subsequent points B and can determine in O(1) time if the rectangle (A, B) is empty by keeping track of the maximum y-coordinate seen so far.
Algorithm
- Sort the
pointsarray. The primary sorting key is the x-coordinate in ascending order. The secondary key is the y-coordinate in descending order. - Initialize a counter
countto 0. - Iterate through the sorted points with an outer loop for
ifrom0ton-1. LetA = points[i]. - Inside this loop, initialize a variable
max_y = -1. This will track the maximum y-coordinate of points encountered so far in the inner loop. - Start an inner loop for
jfromi+1ton-1. LetB = points[j]. - Due to sorting,
A.x <= B.xis always true. We check ifB.y <= A.yto see ifAis upper-left ofB. - If
B.y <= A.y, we then check ifB.y > max_y. If this is true, it means no intermediate pointC = points[k](withi < k < j) can block the view fromAtoB, because all their y-coordinates are less than or equal tomax_y, which is less thanB.y. Thus, the pair(A, B)is valid, and we incrementcount. - In every iteration of the inner loop, update
max_y = max(max_y, B.y). This is because the current pointBcan act as an interfering point for subsequent pairs. - After the loops complete, return
count.
Walkthrough
The key to this optimization is sorting. We sort the points array first by x-coordinate (ascending) and then by y-coordinate (descending). This ordering ensures that if we pick A = points[i] and B = points[j] with i < j, then A.x <= B.x is guaranteed.
The problem then reduces to finding pairs (A, B) where A.y >= B.y, and there is no intermediate point C = points[k] (with i < k < j) such that B.y <= C.y <= A.y.
We can solve this with a double loop. The outer loop fixes A = points[i]. The inner loop iterates through j from i+1 to n-1, considering each B = points[j] as a potential partner. To efficiently check for an interfering point C, we maintain a variable max_y which stores the maximum y-coordinate of all points visited so far in the inner loop (points[i+1] to points[j-1]).
When we consider a new point B = points[j], if B.y <= A.y (making it a potential partner) and B.y > max_y, it implies that all intermediate points C have C.y <= max_y < B.y. Therefore, no C can satisfy C.y >= B.y, meaning the rectangle is empty and the pair (A, B) is valid. We then update max_y with B.y for the next iteration.
import java.util.Arrays; class Solution { public int numberOfPairs(int[][] points) { int n = points.length; Arrays.sort(points, (a, b) -> a[0] != b[0] ? a[0] - b[0] : b[1] - a[1]); int count = 0; for (int i = 0; i < n; i++) { int y1 = points[i][1]; int maxY = -1; // Max y seen for k between i and j for (int j = i + 1; j < n; j++) { int y2 = points[j][1]; // A = points[i], B = points[j] // A is upper-left of B if y1 >= y2 (since x1 <= x2 is guaranteed by sort) // Rectangle is empty if no C=points[k] (i<k<j) has y2 <= C.y <= y1 // maxY stores max(points[k].y) for k in (i+1, j). // If y2 > maxY, then for all k in (i+1,j), points[k].y <= maxY < y2. // So no intermediate point can be >= y2. The condition is met. if (y2 <= y1) { if (y2 > maxY) { count++; } } // Update maxY with the y-coordinate of the current point B (which is points[j]) // because it can act as an interfering point for subsequent points in the inner loop. maxY = Math.max(maxY, y2); } } return count; }}Complexity
Time
O(n^2). The initial sorting step takes O(n log n) time. The subsequent nested loops run in O(n^2) time. The overall complexity is dominated by the nested loops, resulting in O(n^2).
Space
O(log n) or O(n), depending on the space complexity of the sorting algorithm used by the language's standard library. For instance, Java's `Arrays.sort` for primitives uses a Dual-Pivot Quicksort which has an average space complexity of O(log n).
Trade-offs
Pros
Highly efficient with O(n^2) time complexity, which is optimal for this problem.
Scales well even if constraints were moderately larger.
Cons
The logic is more subtle and less direct than the brute-force approach.
Requires a custom sorting comparator, adding a small amount of implementation complexity.
Solutions
Solution
public class Solution { public int NumberOfPairs(int[][] points) { Array.Sort(points, (a, b) => a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]); int ans = 0; int n = points.Length; int inf = 1 << 30; for (int i = 0; i < n; ++i) { int y1 = points[i][1]; int maxY = -inf; for (int j = i + 1; j < n; ++j) { int y2 = points[j][1]; if (maxY < y2 && y2 <= y1) { maxY = y2; ++ans; } } } return ans; }}class Solution { public int numberOfPairs ( int [][] points ) { Arrays . sort ( points , ( a , b ) -> a [ 0 ] == b [ 0 ] ? b [ 1 ] - a [ 1 ] : a [ 0 ] - b [ 0 ]); int ans = 0 ; int n = points . length ; final int inf = 1 << 30 ; for ( int i = 0 ; i < n ; ++ i ) { int y1 = points [ i ][ 1 ]; int maxY = - inf ; for ( int j = i + 1 ; j < n ; ++ j ) { int y2 = points [ j ][ 1 ]; if ( maxY < y2 && y2 <= y1 ) { maxY = y2 ; ++ ans ; } } } return ans ; } }class Solution { public: int numberOfPairs ( vector < vector < int >>& points ) { sort ( points . begin (), points . end (), []( const vector < int >& a , const vector < int >& b ) { return a [ 0 ] < b [ 0 ] || ( a [ 0 ] == b [ 0 ] && b [ 1 ] < a [ 1 ]); }); int n = points . size (); int ans = 0 ; for ( int i = 0 ; i < n ; ++ i ) { int y1 = points [ i ][ 1 ]; int maxY = INT_MIN ; for ( int j = i + 1 ; j < n ; ++ j ) { int y2 = points [ j ][ 1 ]; if ( maxY < y2 && y2 <= y1 ) { maxY = y2 ; ++ ans ; } } } return ans ; } };class Solution : def numberOfPairs ( self , points : List [ List [ int ]]) -> int : points . sort ( key = lambda x : ( x [ 0 ], - x [ 1 ])) ans = 0 for i , ( _ , y1 ) in enumerate ( points ): max_y = - inf for _ , y2 in points [ i + 1 :]: if max_y < y2 <= y1 : max_y = y2 ans += 1 return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.