LeetCode 2543 - Check if Point Is Reachable
The problem gives us an infinite two dimensional grid and asks whether we can travel from the starting point (1, 1) to a target point (targetX, targetY) using a specific set of operations.
Difficulty: 🔴 Hard
Topics: Math, Number Theory
Solution
Problem Understanding
The problem gives us an infinite two dimensional grid and asks whether we can travel from the starting point (1, 1) to a target point (targetX, targetY) using a specific set of operations.
From any point (x, y), we may perform one of four moves:
- Subtract
xfromy, producing(x, y - x) - Subtract
yfromx, producing(x - y, y) - Double
x, producing(2x, y) - Double
y, producing(x, 2y)
The task is to determine whether the target point can be reached after some finite number of operations.
The input consists of two integers:
targetX, the target x-coordinatetargetY, the target y-coordinate
The output is a boolean:
trueif the target is reachablefalseotherwise
The constraints are very important:
1 <= targetX, targetY <= 10^9
Because the coordinates may be as large as one billion, any brute force search through the state space is impossible. The grid is infinite, and the number of reachable intermediate states grows extremely quickly.
A few observations immediately stand out:
- Coordinates always remain integers.
- The subtraction operations resemble the Euclidean algorithm for computing the greatest common divisor.
- Doubling operations only introduce powers of two.
- Reachability likely depends on some invariant property that never changes during valid moves.
Important edge cases include:
- When one coordinate is
1 - When both coordinates are equal
- Very large powers of two
- Pairs whose greatest common divisor contains odd factors
- Minimal input
(1,1)
These cases help expose the mathematical structure behind the problem.
Approaches
Brute Force Approach
A natural first idea is to model the problem as a graph search. Each point (x, y) is a node, and every allowed operation creates an edge to another node.
Starting from (1,1), we could use BFS or DFS to explore reachable states until we either:
- Reach
(targetX, targetY) - Exhaust all possible states
This approach is correct because graph traversal systematically explores every valid sequence of operations.
However, this method is completely impractical.
The coordinates may grow extremely large because of repeated doubling operations. Even if we impose bounds such as limiting coordinates to at most targetX and targetY, the number of states is still enormous. A BFS would potentially examine billions of points.
Additionally, subtraction operations create many overlapping paths and cycles, making the state graph highly connected.
Therefore, brute force cannot work within the constraints.
Key Insight
The key insight is that the operations preserve a very specific mathematical property involving the greatest common divisor.
Consider the subtraction operations:
gcd(x, y) = gcd(x, y - x)
gcd(x, y) = gcd(x - y, y)
These are exactly the transformations used in the Euclidean algorithm.
Now consider the doubling operations:
gcd(2x, y)
gcd(x, 2y)
Doubling may multiply the gcd by 2, but it cannot introduce any odd prime factor.
Since we start from (1,1), whose gcd is:
gcd(1,1) = 1
the only factors we can ever introduce into the gcd are powers of two.
That means:
gcd(targetX, targetY)
must be a power of two for the target to be reachable.
This turns the entire problem into a simple number theory check.
If the gcd is:
1, 2, 4, 8, 16, ...
then the point is reachable.
Otherwise, it is impossible.
We can check whether a number is a power of two using the classic bit trick:
g & (g - 1) == 0
where g is the gcd.
Approach Comparison
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | Exponential | Exponential | Explores enormous state graph of reachable coordinates |
| Optimal | O(log(min(targetX, targetY))) | O(1) | Uses gcd invariant and power-of-two property |
Algorithm Walkthrough
Optimal Algorithm
- Compute the greatest common divisor of
targetXandtargetY.
The subtraction operations preserve the gcd, while doubling operations only multiply it by powers of two. Therefore, the gcd completely determines reachability.
2. Store the gcd in a variable g.
For example:
g = gcd(targetX, targetY)
- Check whether
gis a power of two.
A positive integer is a power of two if and only if:
g & (g - 1) == 0
This works because powers of two contain exactly one set bit in binary form. 4. Return the result of that check.
If the gcd is a power of two, return true.
Otherwise, return false.
Why it works
The crucial invariant is that subtraction operations never change the gcd, and doubling operations can only multiply the gcd by 2.
Since we begin with gcd 1, the only possible gcd values reachable are:
1 * 2^k
for some nonnegative integer k.
Therefore, a target point is reachable if and only if its gcd is a power of two.
This condition is both necessary and sufficient.
Python Solution
from math import gcd
class Solution:
def isReachable(self, targetX: int, targetY: int) -> bool:
common_divisor = gcd(targetX, targetY)
return (common_divisor & (common_divisor - 1)) == 0
The implementation is very compact because the mathematical insight eliminates the need for simulation or graph traversal.
First, we import gcd from Python's math module. This computes the greatest common divisor efficiently using the Euclidean algorithm.
Next, we compute:
common_divisor = gcd(targetX, targetY)
This extracts the invariant property that determines reachability.
Finally, we check whether the gcd is a power of two using:
(common_divisor & (common_divisor - 1)) == 0
A power of two has exactly one set bit in binary representation. Subtracting one flips all lower bits, so the bitwise AND becomes zero only for powers of two.
The result of that expression is returned directly.
Go Solution
func isReachable(targetX int, targetY int) bool {
commonDivisor := gcd(targetX, targetY)
return (commonDivisor & (commonDivisor - 1)) == 0
}
func gcd(a int, b int) int {
for b != 0 {
a, b = b, a%b
}
return a
}
The Go version follows the same logic as the Python solution.
Since Go does not provide a built in gcd function for integers in the standard library, we implement the Euclidean algorithm manually.
The bitwise power of two check works identically in Go because integers support bit operations directly.
There are no overflow concerns because the constraints are at most 10^9, which easily fits inside Go's int type on modern platforms.
Worked Examples
Example 1
Input: targetX = 6, targetY = 9
First compute the gcd:
gcd(6, 9) = 3
Now check whether 3 is a power of two.
Binary representation:
3 = 011
2 = 010
Bitwise AND:
3 & 2 = 2
Since the result is not zero, 3 is not a power of two.
| Step | Value |
|---|---|
| targetX | 6 |
| targetY | 9 |
| gcd | 3 |
| Power of two? | No |
| Result | false |
Final answer:
false
Example 2
Input: targetX = 4, targetY = 7
Compute the gcd:
gcd(4, 7) = 1
Now check whether 1 is a power of two.
Binary representation:
1 = 001
0 = 000
Bitwise AND:
1 & 0 = 0
Therefore, 1 is a power of two.
| Step | Value |
|---|---|
| targetX | 4 |
| targetY | 7 |
| gcd | 1 |
| Power of two? | Yes |
| Result | true |
Final answer:
true
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(log(min(targetX, targetY))) | Euclidean algorithm complexity |
| Space | O(1) | Only a few variables are used |
The dominant operation is computing the greatest common divisor using the Euclidean algorithm. Each modulo operation significantly reduces the problem size, giving logarithmic complexity.
The algorithm uses constant extra memory because it stores only a few integers regardless of input size.
Test Cases
solution = Solution()
assert solution.isReachable(6, 9) == False # gcd is 3, not power of two
assert solution.isReachable(4, 7) == True # gcd is 1
assert solution.isReachable(1, 1) == True # starting position
assert solution.isReachable(2, 2) == True # gcd is 2
assert solution.isReachable(4, 4) == True # gcd is 4
assert solution.isReachable(8, 8) == True # gcd is 8
assert solution.isReachable(3, 3) == False # gcd is 3
assert solution.isReachable(5, 10) == False # gcd is 5
assert solution.isReachable(12, 18) == False # gcd is 6
assert solution.isReachable(1, 1024) == True # large power of two
assert solution.isReachable(1024, 1) == True # asymmetric coordinates
assert solution.isReachable(999999937, 1) == True # large prime with gcd 1
assert solution.isReachable(1000000000, 500000000) == False # gcd has odd factor
assert solution.isReachable(536870912, 536870912) == True # large power of two gcd
Test Case Summary
| Test | Why |
|---|---|
(6, 9) |
gcd contains odd factor |
(4, 7) |
gcd is 1 |
(1, 1) |
starting point |
(2, 2) |
smallest nontrivial power of two gcd |
(4, 4) |
larger power of two gcd |
(8, 8) |
repeated doubling case |
(3, 3) |
odd gcd |
(5, 10) |
gcd is odd prime |
(12, 18) |
mixed even and odd gcd |
(1, 1024) |
one coordinate fixed |
(1024, 1) |
asymmetric case |
(999999937, 1) |
very large prime coordinate |
(1000000000, 500000000) |
large invalid gcd |
(536870912, 536870912) |
very large power of two |
Edge Cases
One important edge case is the starting point itself, (1,1). A careless implementation might incorrectly assume some movement is required before success. Here, the gcd is 1, which is a power of two, so the algorithm correctly returns true.
Another important edge case occurs when both coordinates are equal, such as (8,8) or (3,3). In these cases, the gcd equals the coordinate value itself. This makes the result entirely dependent on whether that value is a power of two. The implementation handles this naturally through the gcd check.
A third critical edge case involves very large inputs near the upper constraint limit. A brute force search would fail completely for values near 10^9, either timing out or exhausting memory. The mathematical solution remains efficient because the Euclidean algorithm runs in logarithmic time even for huge numbers.
A subtle edge case occurs when the gcd is even but not a pure power of two, such as 6 or 10. It may initially seem that doubling operations could create such values, but doubling only introduces factors of two. Odd prime factors can never appear after starting from gcd 1. The implementation correctly rejects these cases because the power of two check fails.