Find the Number of Ways to Place People II
HardPrompt
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].
We define the right direction as positive x-axis (increasing x-coordinate) and the left direction as negative x-axis (decreasing x-coordinate). Similarly, we define the up direction as positive y-axis (increasing y-coordinate) and the down direction as negative y-axis (decreasing y-coordinate)
You have to place n people, including Alice and Bob, at these points such that there is exactly one person at every point. Alice wants to be alone with Bob, so Alice will build a rectangular fence with Alice's position as the upper left corner and Bob's position as the lower right corner of the fence (Note that the fence might not enclose any area, i.e. it can be a line). If any person other than Alice and Bob is either inside the fence or on the fence, Alice will be sad.
Return the number of pairs of points where you can place Alice and Bob, such that Alice does not become sad on building the fence.
Note that Alice can only build a fence with Alice's position as the upper left corner, and Bob's position as the lower right corner. For example, Alice cannot build either of the fences in the picture below with four corners (1, 1), (1, 3), (3, 1), and (3, 3), because:
- With Alice at
(3, 3)and Bob at(1, 1), Alice's position is not the upper left corner and Bob's position is not the lower right corner of the fence. - With Alice at
(1, 3)and Bob at(1, 1), Bob's position is not the lower right corner of the fence.
Example 1:
Input: points = [[1,1],[2,2],[3,3]]
Output: 0
Explanation: There is no way to place Alice and Bob such that Alice can build a fence with Alice's position as the upper left corner and Bob's position as the lower right corner. Hence we return 0. Example 2:
Input: points = [[6,2],[4,4],[2,6]]
Output: 2
Explanation: There are two ways to place Alice and Bob such that Alice will not be sad:
- Place Alice at (4, 4) and Bob at (6, 2).
- Place Alice at (2, 6) and Bob at (4, 4).
You cannot place Alice at (2, 6) and Bob at (6, 2) because the person at (4, 4) will be inside the fence.Example 3:
Input: points = [[3,1],[1,3],[1,1]]
Output: 2
Explanation: There are two ways to place Alice and Bob such that Alice will not be sad:
- Place Alice at (1, 1) and Bob at (3, 1).
- Place Alice at (1, 3) and Bob at (1, 1).
You cannot place Alice at (1, 3) and Bob at (3, 1) because the person at (1, 1) will be on the fence.
Note that it does not matter if the fence encloses any area, the first and second fences in the image are valid.
Constraints:
2 <= n <= 1000points[i].length == 2-109 <= points[i][0], points[i][1] <= 109- All
points[i]are distinct.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach directly translates the problem statement into code. It examines every possible pair of points for Alice and Bob. For each potential pair that satisfies the upper-left/lower-right condition, it then checks every other point to see if it falls within the rectangular fence. This method is straightforward but computationally expensive.
Algorithm
- Initialize a counter
countto 0. - Iterate through each point
points[i]as a potential location for Alice. - Inside this loop, iterate through each point
points[j]as a potential location for Bob, ensuringiis not equal toj. - Let
alice = points[i]andbob = points[j]. - Check if Alice is at an upper-left position relative to Bob:
alice.x <= bob.xandalice.y >= bob.y. If not, this pair is invalid, so continue to the next pair. - If the position condition holds, assume the pair is valid by setting a flag
is_sad = false. - Start a third loop to check for interfering points. Iterate through all other points
points[k](wherekis notiorj). - For each point
p = points[k], check if it lies inside or on the fence defined by Alice and Bob:alice.x <= p.x <= bob.xandbob.y <= p.y <= alice.y. - If an interfering point is found, set
is_sad = trueand break the innermost loop. - After checking all other points, if
is_sadremainsfalse, it means the pair(alice, bob)is valid. Increment thecount. - After all loops complete, return the total
count.
Walkthrough
The brute-force algorithm systematically checks all possibilities. It uses three nested loops. The outer two loops select a pair of distinct points for Alice and Bob. The first check ensures that Alice's point (x_A, y_A) and Bob's point (x_B, y_B) satisfy the geometric requirement x_A <= x_B and y_A >= y_B. If they do, the third loop iterates through all remaining n-2 points to verify that none of them are inside or on the boundary of the rectangle formed by Alice and Bob. A pair is counted only if this 'emptiness' condition is met.
class Solution { public int numberOfPairs(int[][] points) { int n = points.length; int count = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i == j) continue; int[] alice = points[i]; int[] bob = points[j]; if (alice[0] <= bob[0] && alice[1] >= bob[1]) { boolean isSad = false; for (int k = 0; k < n; k++) { if (k == i || k == j) continue; int[] p = points[k]; if (p[0] >= alice[0] && p[0] <= bob[0] && p[1] >= bob[1] && p[1] <= alice[1]) { isSad = true; break; } } if (!isSad) { count++; } } } } return count; }}Complexity
Time
O(N³), where N is the number of points. The three nested loops each run up to N times, leading to a cubic time complexity.
Space
O(1) extra space, as it only requires a few variables for loops and counting.
Trade-offs
Pros
Simple to understand and implement as it follows the problem's definition directly.
Cons
Highly inefficient due to its cubic time complexity.
Will likely result in a 'Time Limit Exceeded' error for larger inputs, such as the
n=1000constraint.
This optimized approach significantly improves performance by using a clever sorting strategy. By sorting the points first by their x-coordinate (and y-coordinate for ties), we can establish a fixed order for considering pairs. This allows us to check for interfering points in a much more efficient O(N²) manner, avoiding the third nested loop of the brute-force method.
Algorithm
- First, sort the
pointsarray. The primary sorting key is the x-coordinate in ascending order. The secondary key for ties in x-coordinates is the y-coordinate in descending order. - Initialize a counter
countto 0. - Iterate with an outer loop from
i = 0ton-1. The pointp_iatpoints[i]will be considered as Alice's location. - For each
p_i, initialize a variablemaxYUnderAliceto a very small number (e.g.,Integer.MIN_VALUE). This will track the maximum y-coordinate of intermediate points that are valid blockers. - Start an inner loop from
j = i + 1ton-1. The pointp_jatpoints[j]is a potential location for Bob or an intermediate point. - Let
aliceY = p_i[1]andbobY = p_j[1]. - Check if
p_jis 'below'p_i(i.e.,bobY <= aliceY). Points abovep_ican't be Bob and can't block the view in the required manner. - If
bobY <= aliceY, check ifp_jis a valid Bob. It is valid if it's not blocked by any previous intermediate point that was also below Alice. This condition is met ifbobY > maxYUnderAlice. - If both conditions (
bobY <= aliceYandbobY > maxYUnderAlice) are true, incrementcount. - After the check, update
maxYUnderAlice = max(maxYUnderAlice, bobY)to account forp_jas a potential blocker for subsequent points. - After the loops complete, return the total
count.
Walkthrough
The core idea is to reduce the complexity of checking for interfering points. We sort the points by x-coordinate (ascending) and then by y-coordinate (descending). This ensures that if we pick p_i for Alice and p_j for Bob with i < j, the condition x_i <= x_j is automatically satisfied.
The problem then simplifies to finding pairs (p_i, p_j) with i < j such that y_i >= y_j, and no intermediate point p_k (with i < k < j) lies inside the fence. An intermediate point p_k is inside the fence if y_j <= y_k <= y_i.
To solve this efficiently, we iterate through each point p_i as Alice. For each p_i, we iterate through all subsequent points p_j (j > i). We only need to consider p_j where y_j <= y_i. For such a p_j to be a valid Bob, no intermediate point p_k that is also 'below' Alice (y_k <= y_i) can block it. A point p_k blocks p_j if y_k >= y_j. This means a valid p_j must have a y-coordinate greater than the y-coordinates of all preceding intermediate points that are also below Alice. We can track this with a single variable, maxYUnderAlice.
import java.util.Arrays; class Solution { public int numberOfPairs(int[][] points) { Arrays.sort(points, (a, b) -> { if (a[0] != b[0]) { return a[0] - b[0]; } else { return b[1] - a[1]; } }); int n = points.length; int count = 0; for (int i = 0; i < n; i++) { int aliceY = points[i][1]; int maxYUnderAlice = Integer.MIN_VALUE; for (int j = i + 1; j < n; j++) { int bobY = points[j][1]; // We only consider points 'below' Alice as potential Bobs or blockers. if (bobY <= aliceY) { // A valid Bob must have a y-coordinate greater than any // intermediate point that was also below Alice. if (bobY > maxYUnderAlice) { count++; } // Update the max y-coordinate of points seen so far that are below Alice. maxYUnderAlice = Math.max(maxYUnderAlice, bobY); } } } return count; }}Complexity
Time
O(N²). The initial sort takes O(N log N) time. The nested loops contribute O(N²) time, which is the dominant factor.
Space
O(log N) or O(N), depending on the implementation of the sorting algorithm. The rest of the algorithm uses O(1) extra space.
Trade-offs
Pros
Much more efficient with O(N²) time complexity, which is suitable for the given constraints.
The core logic is elegant and avoids complex data structures.
Cons
The logic, especially the sorting criteria and the single-pass check, is less intuitive than the brute-force method.
Requires careful implementation to handle the state (
maxYUnderAlice) correctly.
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.