questions C
This commit is contained in:
161
backend/data/questions/check-if-point-is-reachable.yaml
Normal file
161
backend/data/questions/check-if-point-is-reachable.yaml
Normal file
@@ -0,0 +1,161 @@
|
||||
title: Check if Point Is Reachable
|
||||
slug: check-if-point-is-reachable
|
||||
difficulty: hard
|
||||
leetcode_id: 2543
|
||||
leetcode_url: https://leetcode.com/problems/check-if-point-is-reachable/
|
||||
categories:
|
||||
- math
|
||||
patterns:
|
||||
- greedy
|
||||
|
||||
description: |
|
||||
There exists an infinitely large grid. You are currently at point `(1, 1)`, and you need to reach the point `(targetX, targetY)` using a finite number of steps.
|
||||
|
||||
In one **step**, you can move from point `(x, y)` to any one of the following points:
|
||||
|
||||
- `(x, y - x)`
|
||||
- `(x - y, y)`
|
||||
- `(2 * x, y)`
|
||||
- `(x, 2 * y)`
|
||||
|
||||
Given two integers `targetX` and `targetY` representing the X-coordinate and Y-coordinate of your final position, return `true` *if you can reach the point from* `(1, 1)` *using some number of steps, and* `false` *otherwise*.
|
||||
|
||||
constraints: |
|
||||
- `1 <= targetX, targetY <= 10^9`
|
||||
|
||||
examples:
|
||||
- input: "targetX = 6, targetY = 9"
|
||||
output: "false"
|
||||
explanation: "It is impossible to reach (6, 9) from (1, 1) using any sequence of moves, so false is returned."
|
||||
- input: "targetX = 4, targetY = 7"
|
||||
output: "true"
|
||||
explanation: "You can follow the path (1, 1) -> (1, 2) -> (1, 4) -> (1, 8) -> (1, 7) -> (2, 7) -> (4, 7)."
|
||||
|
||||
explanation:
|
||||
intuition: |
|
||||
This problem initially seems overwhelming — the grid is infinite, the target can be up to 10<sup>9</sup>, and we have four different moves. Simulating all possible paths is clearly impossible.
|
||||
|
||||
The breakthrough comes from **thinking backwards**. Instead of asking "can we reach `(targetX, targetY)` from `(1, 1)`?", ask "can we reach `(1, 1)` from `(targetX, targetY)` using the **reverse** operations?"
|
||||
|
||||
The reverse operations are:
|
||||
- `(x, y)` came from `(x, x + y)` — reverse of `(x, y - x)`
|
||||
- `(x, y)` came from `(x + y, y)` — reverse of `(x - y, y)`
|
||||
- `(x, y)` came from `(x / 2, y)` if `x` is even — reverse of `(2 * x, y)`
|
||||
- `(x, y)` came from `(x, y / 2)` if `y` is even — reverse of `(x, 2 * y)`
|
||||
|
||||
Now notice something remarkable: the first two reverse operations `(x, x + y)` and `(x + y, y)` are exactly the operations in the **Euclidean algorithm** for computing GCD! If we keep applying these operations, we eventually reach `(gcd(x, y), gcd(x, y))`.
|
||||
|
||||
The last two operations let us **divide by 2** as many times as we want. So starting from `(targetX, targetY)`:
|
||||
1. We can reduce to `(gcd(targetX, targetY), gcd(targetX, targetY))` using the GCD operations
|
||||
2. We can then divide by 2 repeatedly to reach `(1, 1)`
|
||||
|
||||
This only works if `gcd(targetX, targetY)` is a **power of 2** (including 2<sup>0</sup> = 1). If the GCD has any odd factor greater than 1, we can never eliminate it.
|
||||
|
||||
approach: |
|
||||
We solve this using **GCD and bit manipulation**:
|
||||
|
||||
**Step 1: Compute the GCD**
|
||||
|
||||
- Calculate `g = gcd(targetX, targetY)` using the Euclidean algorithm
|
||||
- This represents the value we must reduce to `(g, g)` before we can reach `(1, 1)`
|
||||
|
||||
|
||||
|
||||
**Step 2: Check if GCD is a power of 2**
|
||||
|
||||
- A number is a power of 2 if and only if it has exactly one bit set
|
||||
- Use the classic bit trick: `g & (g - 1) == 0` returns `true` for powers of 2
|
||||
- Alternatively, check that `g` has no odd factors (continuously divide by 2 until odd)
|
||||
|
||||
|
||||
|
||||
**Step 3: Return the result**
|
||||
|
||||
- If the GCD is a power of 2, return `true`
|
||||
- Otherwise, return `false`
|
||||
|
||||
|
||||
|
||||
The elegance of this solution is that we reduced a seemingly complex pathfinding problem to a simple mathematical property check.
|
||||
|
||||
common_pitfalls:
|
||||
- title: Attempting BFS/DFS Simulation
|
||||
description: |
|
||||
The instinct to simulate paths using BFS or DFS fails catastrophically here. With coordinates up to 10<sup>9</sup> and infinite branching possibilities, any simulation approach will either run out of memory or time.
|
||||
|
||||
The problem *looks* like a graph traversal but is actually a **number theory** problem in disguise. Recognising when to abandon standard algorithms for mathematical insights is key.
|
||||
wrong_approach: "BFS/DFS to explore all reachable states"
|
||||
correct_approach: "Reverse the problem and analyse GCD properties"
|
||||
|
||||
- title: Missing the Reverse Thinking
|
||||
description: |
|
||||
Working forward from `(1, 1)` is confusing because the state space explodes exponentially. Working backwards from the target is much more tractable because:
|
||||
- Division by 2 shrinks coordinates
|
||||
- The GCD operations have well-understood convergence properties
|
||||
|
||||
Always consider whether reversing the direction of a reachability problem simplifies it.
|
||||
wrong_approach: "Simulate forward from (1, 1)"
|
||||
correct_approach: "Work backwards from (targetX, targetY)"
|
||||
|
||||
- title: Incorrectly Checking Powers of 2
|
||||
description: |
|
||||
Common mistakes when checking if a number is a power of 2:
|
||||
- Forgetting that `1` (2<sup>0</sup>) is a valid power of 2
|
||||
- Using `n % 2 == 0` which only checks if `n` is even, not a power of 2
|
||||
- Using floating-point logarithms which have precision issues
|
||||
|
||||
The bit manipulation trick `n & (n - 1) == 0` is reliable and handles all edge cases (for `n > 0`).
|
||||
wrong_approach: "log2(n) is an integer check"
|
||||
correct_approach: "Bit manipulation: n & (n - 1) == 0"
|
||||
|
||||
key_takeaways:
|
||||
- "**Reverse the problem**: When forward simulation is intractable, working backwards often reveals structure"
|
||||
- "**Recognise GCD operations**: The operations `(x, x + y)` and `(x + y, y)` are the Euclidean algorithm — this pattern appears in many problems"
|
||||
- "**Powers of 2 and bit tricks**: `n & (n - 1) == 0` is the standard way to check if `n` is a power of 2"
|
||||
- "**Number theory in disguise**: Some problems that look like graph traversal are actually about mathematical invariants"
|
||||
|
||||
time_complexity: "O(log(min(targetX, targetY))). Computing GCD using the Euclidean algorithm takes logarithmic time."
|
||||
space_complexity: "O(1). We only use a constant number of variables."
|
||||
|
||||
solutions:
|
||||
- approach_name: GCD Power of 2 Check
|
||||
is_optimal: true
|
||||
code: |
|
||||
from math import gcd
|
||||
|
||||
def is_reachable(target_x: int, target_y: int) -> bool:
|
||||
# Compute GCD of the target coordinates
|
||||
g = gcd(target_x, target_y)
|
||||
|
||||
# Check if GCD is a power of 2
|
||||
# A number is a power of 2 iff it has exactly one bit set
|
||||
# n & (n - 1) clears the lowest set bit; result is 0 only for powers of 2
|
||||
return g & (g - 1) == 0
|
||||
explanation: |
|
||||
**Time Complexity:** O(log(min(targetX, targetY))) — GCD computation dominates.
|
||||
|
||||
**Space Complexity:** O(1) — Only a few integer variables.
|
||||
|
||||
The key insight is that we can always reach `(gcd(x, y), gcd(x, y))` from `(x, y)` using the reverse GCD operations, and from there we can only reach `(1, 1)` if we can divide by 2 enough times — which requires the GCD to be a power of 2.
|
||||
|
||||
- approach_name: Iterative Division Check
|
||||
is_optimal: false
|
||||
code: |
|
||||
from math import gcd
|
||||
|
||||
def is_reachable(target_x: int, target_y: int) -> bool:
|
||||
# Compute GCD of the target coordinates
|
||||
g = gcd(target_x, target_y)
|
||||
|
||||
# Remove all factors of 2 from GCD
|
||||
while g % 2 == 0:
|
||||
g //= 2
|
||||
|
||||
# If only factors of 2, we end up with 1
|
||||
return g == 1
|
||||
explanation: |
|
||||
**Time Complexity:** O(log(min(targetX, targetY))) — GCD computation plus at most log(g) divisions.
|
||||
|
||||
**Space Complexity:** O(1) — Only integer variables used.
|
||||
|
||||
This approach explicitly removes all factors of 2 from the GCD. If we end up with 1, the original GCD was a power of 2. If we end up with something greater than 1, there was an odd prime factor that we cannot eliminate.
|
||||
Reference in New Issue
Block a user