780. Reaching Points
Problem Description
You are given four integers: starting coordinates (sx, sy)
and target coordinates (tx, ty)
. Your goal is to determine if you can transform the starting point into the target point through a series of allowed operations.
The allowed operations are:
- From any point
(x, y)
, you can move to(x, x + y)
- this adds the x-coordinate to the y-coordinate - From any point
(x, y)
, you can move to(x + y, y)
- this adds the y-coordinate to the x-coordinate
Starting from (sx, sy)
, you need to check if it's possible to reach exactly (tx, ty)
by applying these operations any number of times.
For example:
- Starting from
(1, 1)
, you could go to(1, 2)
using the first operation - From
(1, 2)
, you could go to(3, 2)
using the second operation - From
(3, 2)
, you could go to(3, 5)
using the first operation - And so on...
The function should return true
if the target point is reachable from the starting point, and false
otherwise.
The key insight for solving this efficiently is to work backwards from the target to the source. Since we can only add values going forward, when working backwards we can determine which operation was used by comparing the coordinates - the larger coordinate must have been created by adding the smaller one to it. This allows us to reverse the operations efficiently using modulo operations when the difference is large.
Intuition
The key observation is that moving forward from (sx, sy)
to (tx, ty)
can create an exponentially large search space, making it inefficient to explore all possible paths. However, working backwards from (tx, ty)
to (sx, sy)
is much more efficient.
Why work backwards? When we're at point (tx, ty)
, we can determine exactly which operation was used to reach it:
- If
tx > ty
, the last operation must have been(tx - ty, ty) → (tx, ty)
- If
ty > tx
, the last operation must have been(tx, ty - tx) → (tx, ty)
This gives us a unique path backwards, eliminating the branching factor that exists when moving forward.
The brilliant optimization comes from recognizing that if tx >> ty
(much greater), we would need to subtract ty
from tx
many times repeatedly. Instead of doing this one by one, we can use the modulo operation: tx % ty
gives us the result after all possible subtractions of ty
from tx
. This reduces the time complexity from linear to logarithmic.
For example, if we have (1000, 3)
and need to reach (1, 3)
, instead of subtracting 3 from 1000 repeatedly (333 operations), we can directly compute 1000 % 3 = 1
.
The algorithm stops when:
- We reach
(sx, sy)
exactly - returntrue
- One coordinate matches while the other is still larger - check if the difference is divisible by the matching coordinate
- We go below either
sx
orsy
- impossible to reach, returnfalse
This approach transforms an exponential forward search into an efficient logarithmic backward traversal.
Learn more about Math patterns.
Solution Approach
The implementation uses a working-backwards approach with modulo optimization to efficiently determine if we can reach (sx, sy)
from (tx, ty)
.
Main Loop - Backward Traversal:
while tx > sx and ty > sy and tx != ty: if tx > ty: tx %= ty else: ty %= tx
This loop continues as long as both coordinates are larger than their starting counterparts and they're not equal. In each iteration:
- If
tx > ty
, we know the previous state was(tx - n*ty, ty)
for some positive integern
. Usingtx %= ty
directly computes the result after maximum possible subtractions. - If
ty > tx
, similarly we usety %= tx
to reverse multiple operations at once.
The condition tx != ty
prevents division by zero and handles edge cases where coordinates become equal.
Terminal Cases Check:
After the loop terminates, we have four possible scenarios:
- Exact Match:
if tx == sx and ty == sy: return True
We've successfully traced back to the starting point.
- X-coordinate Matches:
if tx == sx: return ty > sy and (ty - sy) % tx == 0
When tx
equals sx
, we can only perform operations that add tx
to ty
. We check if:
ty > sy
(we haven't gone past the target)(ty - sy) % tx == 0
(the difference is achievable by addingtx
multiple times)
- Y-coordinate Matches:
if ty == sy: return tx > sx and (tx - sx) % ty == 0
Similar logic when ty
equals sy
- we can only add ty
to tx
repeatedly.
- No Match:
return False
If none of the above conditions are met, it's impossible to reach the target.
Time Complexity: O(log(max(tx, ty)))
due to the modulo operations which work similarly to the Euclidean algorithm for GCD.
Space Complexity: O(1)
as we only use a constant amount of extra space.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through an example where we start at (sx, sy) = (1, 1)
and want to reach (tx, ty) = (5, 12)
.
Working Backwards from (5, 12) to (1, 1):
Step 1: Start with (tx, ty) = (5, 12)
- Since
ty > tx
(12 > 5), the previous point must have been(5, 12 - 5) = (5, 7)
- We know this because to get to
(5, 12)
, we must have added 5 to the y-coordinate
Step 2: Now at (5, 7)
- Since
ty > tx
(7 > 5), the previous point was(5, 7 - 5) = (5, 2)
Step 3: Now at (5, 2)
- Since
tx > ty
(5 > 2), the previous point was(5 - 2, 2) = (3, 2)
Step 4: Now at (3, 2)
- Since
tx > ty
(3 > 2), the previous point was(3 - 2, 2) = (1, 2)
Step 5: Now at (1, 2)
- We have
tx = sx = 1
(x-coordinate matches) - Check if we can reach
(1, 1)
from(1, 2)
:- Is
ty > sy
? Yes, 2 > 1 ✓ - Is
(ty - sy) % tx == 0
? Is(2 - 1) % 1 == 0
? Yes, 1 % 1 = 0 ✓
- Is
- Therefore, we can reach
(1, 1)
by one more backward step
Result: Return true
- we successfully traced a path from (5, 12)
back to (1, 1)
.
Optimization with Modulo:
If we had (sx, sy) = (1, 1)
and (tx, ty) = (1000, 3)
, the algorithm would:
- Start with
(1000, 3)
- Since
tx > ty
(1000 > 3), computetx = 1000 % 3 = 1
- Now we have
(1, 3)
- Check terminal case:
tx = sx = 1
matches - Check if
(3 - 1) % 1 == 0
? Yes, so returntrue
This avoided 333 individual subtractions by using the modulo operation!
Solution Implementation
1class Solution:
2 def reachingPoints(self, sx: int, sy: int, tx: int, ty: int) -> bool:
3 """
4 Determine if we can reach target point (tx, ty) from source point (sx, sy).
5 At each step, we can transform (x, y) to either (x + y, y) or (x, x + y).
6
7 The key insight is to work backwards from target to source, since going forward
8 has many possible paths but going backward is deterministic.
9 """
10
11 # Work backwards from target point by reducing the larger coordinate
12 # When tx > ty, the previous step must have been (tx - ty, ty)
13 # We use modulo to skip multiple subtraction steps at once
14 while tx > sx and ty > sy and tx != ty:
15 if tx > ty:
16 # Reduce tx by multiples of ty
17 tx %= ty
18 else:
19 # Reduce ty by multiples of tx
20 ty %= tx
21
22 # Check if we've reached the source point exactly
23 if tx == sx and ty == sy:
24 return True
25
26 # If x-coordinate matches, check if we can reach source by only changing y
27 if tx == sx:
28 # ty must be reachable from sy by adding tx multiple times
29 return ty > sy and (ty - sy) % tx == 0
30
31 # If y-coordinate matches, check if we can reach source by only changing x
32 if ty == sy:
33 # tx must be reachable from sx by adding ty multiple times
34 return tx > sx and (tx - sx) % ty == 0
35
36 # Cannot reach the source point
37 return False
38
1class Solution {
2 /**
3 * Determines if we can reach the target point (tx, ty) from the starting point (sx, sy)
4 * by repeatedly transforming (x, y) to either (x + y, y) or (x, x + y).
5 *
6 * @param sx Starting x-coordinate
7 * @param sy Starting y-coordinate
8 * @param tx Target x-coordinate
9 * @param ty Target y-coordinate
10 * @return true if target point is reachable, false otherwise
11 */
12 public boolean reachingPoints(int sx, int sy, int tx, int ty) {
13 // Work backwards from target to source using modulo operations
14 // When tx > ty, the previous step must have been (tx - ty, ty)
15 // When ty > tx, the previous step must have been (tx, ty - tx)
16 // Using modulo speeds up multiple subtractions
17 while (tx > sx && ty > sy && tx != ty) {
18 if (tx > ty) {
19 // Reduce tx by multiples of ty
20 tx %= ty;
21 } else {
22 // Reduce ty by multiples of tx
23 ty %= tx;
24 }
25 }
26
27 // Check if we've reached the starting point exactly
28 if (tx == sx && ty == sy) {
29 return true;
30 }
31
32 // If x-coordinate matches, check if we can reach target by only adding sx to sy
33 if (tx == sx) {
34 // ty must be reachable by adding multiples of tx to sy
35 return ty > sy && (ty - sy) % tx == 0;
36 }
37
38 // If y-coordinate matches, check if we can reach target by only adding sy to sx
39 if (ty == sy) {
40 // tx must be reachable by adding multiples of ty to sx
41 return tx > sx && (tx - sx) % ty == 0;
42 }
43
44 // Target point is not reachable
45 return false;
46 }
47}
48
1class Solution {
2public:
3 bool reachingPoints(int sx, int sy, int tx, int ty) {
4 // Work backwards from target to source
5 // While both target coordinates are larger than source coordinates
6 // and they are not equal to each other
7 while (tx > sx && ty > sy && tx != ty) {
8 // Reduce the larger coordinate by taking modulo with the smaller one
9 // This simulates multiple subtraction operations in one step
10 if (tx > ty) {
11 tx %= ty;
12 } else {
13 ty %= tx;
14 }
15 }
16
17 // Check if we've reached the exact source point
18 if (tx == sx && ty == sy) {
19 return true;
20 }
21
22 // If x-coordinate matches source, check if we can reach source
23 // by repeatedly subtracting tx from ty
24 if (tx == sx) {
25 return ty > sy && (ty - sy) % tx == 0;
26 }
27
28 // If y-coordinate matches source, check if we can reach source
29 // by repeatedly subtracting ty from tx
30 if (ty == sy) {
31 return tx > sx && (tx - sx) % ty == 0;
32 }
33
34 // Cannot reach the source point from target
35 return false;
36 }
37};
38
1function reachingPoints(sx: number, sy: number, tx: number, ty: number): boolean {
2 // Work backwards from target to source
3 // While both target coordinates are larger than source coordinates
4 // and they are not equal to each other
5 while (tx > sx && ty > sy && tx !== ty) {
6 // Reduce the larger coordinate by taking modulo with the smaller one
7 // This simulates multiple subtraction operations in one step
8 if (tx > ty) {
9 tx %= ty;
10 } else {
11 ty %= tx;
12 }
13 }
14
15 // Check if we've reached the exact source point
16 if (tx === sx && ty === sy) {
17 return true;
18 }
19
20 // If x-coordinate matches source, check if we can reach source
21 // by repeatedly subtracting tx from ty
22 if (tx === sx) {
23 return ty > sy && (ty - sy) % tx === 0;
24 }
25
26 // If y-coordinate matches source, check if we can reach source
27 // by repeatedly subtracting ty from tx
28 if (ty === sy) {
29 return tx > sx && (tx - sx) % ty === 0;
30 }
31
32 // Cannot reach the source point from target
33 return false;
34}
35
Time and Space Complexity
Time Complexity: O(log(max(tx, ty)))
The algorithm works backwards from (tx, ty)
to (sx, sy)
. In each iteration of the while loop, we perform a modulo operation that significantly reduces the larger value:
- When
tx > ty
, we performtx %= ty
, which reducestx
to at mostty - 1
- When
ty > tx
, we performty %= tx
, which reducesty
to at mosttx - 1
This reduction pattern is similar to the Euclidean algorithm for finding GCD. In the worst case, the number of iterations is proportional to the logarithm of the maximum of tx
and ty
. Each modulo operation takes O(1)
time, resulting in an overall time complexity of O(log(max(tx, ty)))
.
Space Complexity: O(1)
The algorithm uses only a constant amount of extra space. We modify the input values tx
and ty
in-place without using any additional data structures or recursive calls. All operations are performed using the existing variables, requiring only O(1)
auxiliary space.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Infinite Loop When tx == ty
The Pitfall:
If we don't include the condition tx != ty
in the while loop, the code will crash or enter an infinite loop when the coordinates become equal. For example, if we reach a state where tx = ty = 5
, then both tx %= ty
and ty %= tx
would attempt division by zero after the modulo operation reduces one coordinate to 0.
Solution:
Always include the tx != ty
check in the loop condition:
while tx > sx and ty > sy and tx != ty: # tx != ty prevents division by zero
2. Incorrect Terminal Case Validation
The Pitfall:
A common mistake is to check only (ty - sy) % tx == 0
without verifying ty > sy
(or similarly for the x-coordinate case). This could incorrectly return True
for cases where we've "overshot" the target in the backward traversal.
For example, if sx = 3, sy = 7, tx = 3, ty = 4
, without the ty > sy
check, we might incorrectly validate (4 - 7) % 3 == 0
which gives True
since -3 % 3 == 0
, but we cannot actually reach (3, 4)
from (3, 7)
going forward.
Solution: Always verify both conditions in terminal cases:
if tx == sx: return ty > sy and (ty - sy) % tx == 0 # Must check ty > sy if ty == sy: return tx > sx and (tx - sx) % ty == 0 # Must check tx > sx
3. Not Handling Edge Cases Where Source Equals Target
The Pitfall:
The algorithm might not immediately handle the case where (sx, sy) == (tx, ty)
if the implementation doesn't check for this condition before or during the main loop.
Solution:
The current implementation handles this naturally - when source equals target, the while loop doesn't execute, and the first condition if tx == sx and ty == sy: return True
catches this case. However, for clarity, you could add an early return:
if sx == tx and sy == ty: return True # Already at target
4. Using Subtraction Instead of Modulo
The Pitfall: Using repeated subtraction instead of modulo operation:
# Inefficient approach - DO NOT USE while tx > ty: tx -= ty # This could take millions of iterations
This would cause Time Limit Exceeded (TLE) for large differences between coordinates, such as tx = 1000000000, ty = 1
.
Solution: Always use modulo for efficiency:
if tx > ty: tx %= ty # Reduces tx in O(1) time
This reduces the time complexity from potentially O(max(tx, ty)) to O(log(max(tx, ty))).
Is the following code DFS or BFS?
void search(Node root) { if (!root) return; visit(root); root.visited = true; for (Node node in root.adjacent) { if (!node.visited) { search(node); } } }
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!