Facebook Pixel

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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. We reach (sx, sy) exactly - return true
  2. One coordinate matches while the other is still larger - check if the difference is divisible by the matching coordinate
  3. We go below either sx or sy - impossible to reach, return false

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 integer n. Using tx %= ty directly computes the result after maximum possible subtractions.
  • If ty > tx, similarly we use ty %= 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:

  1. Exact Match:
if tx == sx and ty == sy:
    return True

We've successfully traced back to the starting point.

  1. 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 adding tx multiple times)
  1. 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.

  1. 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 Evaluator

Example 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 ✓
  • 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:

  1. Start with (1000, 3)
  2. Since tx > ty (1000 > 3), compute tx = 1000 % 3 = 1
  3. Now we have (1, 3)
  4. Check terminal case: tx = sx = 1 matches
  5. Check if (3 - 1) % 1 == 0? Yes, so return true

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 perform tx %= ty, which reduces tx to at most ty - 1
  • When ty > tx, we perform ty %= tx, which reduces ty to at most tx - 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))).

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

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

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More