Frog Jump

Updated on 30 May, 2025
Frog Jump header image

Problem Statement

In this problem, a frog is attempting to cross a river distinguished by a series of stones laid out in an ordered linear sequence. Each stone represents a unit where the frog can land, and the absence of a stone represents a span of water. The game starts with the frog placed on the first stone, and it must reach the final stone using a series of jumps. The length of the initial jump is always 1 unit.

As the frog makes a jump of k units, its subsequent jump can either be k-1, k, or k+1 units long, provided the target stone exists. The primary challenge is to determine if the frog can successfully land on the last stone by making a series of valid jumps. Missing a stone, which would equate to landing in the water, is not allowed.

Examples

Example 1

Input:

stones = [0,1,3,5,6,8,12,17]

Output:

true

Explanation:

The frog can jump to the last stone by jumping 1 unit to the 2nd stone, then 2 units to the 3rd stone, then 2 units to the 4th stone, then 3 units to the 6th stone, 4 units to the 7th stone, and 5 units to the 8th stone.

Example 2

Input:

stones = [0,1,2,3,4,8,9,11]

Output:

false

Explanation:

There is no way to jump to the last stone as the gap between the 5th and 6th stone is too large.

Constraints

  • 2 <= stones.length <= 2000
  • 0 <= stones[i] <= 231 - 1
  • stones[0] == 0
  • stones is sorted in a strictly increasing order.

Approach and Intuition

To solve this problem, consider the following approach based on the dynamic programming paradigm, specifically using recursive state exploration with memorization:

  1. Initialization:

    • Start from the first stone and always begin with a jump of 1 unit.
    • Use a data structure, such as a dictionary or an array, to keep track of each stone's position and the applicable jump distances from that stone.
  2. Recursive Exploration:

    • For each stone, explore all possible jumps (i.e., k-1, k, k+1) that land on subsequent stones.
    • Continue this process from each newly landed stone using the same set of potential jumps updated based on the most recent jump distance.
  3. Memorization:

    • Implement memorization to store already computed results of possible positions and jumps to avoid redundant calculations and minimize time complexity.
  4. Termination Check:

    • If the frog can land on the final stone, return true.
    • If the stone sequences or jump possibilities exhaust without reaching the final stone, return false.

By using the recursive examination of each stone's possibilities, combined with careful tracking and a backtrack mechanism to explore alternate paths, this approach effectively explores all potent pathways the frog can take to ascertain its success in crossing the river. The inherent challenge lies in identifying the most optimized path which respects the frog's specific jumping constraints and the spatial configuration of the stones.

Solutions

  • C++
  • Java
cpp
class Solution {
public:
    unordered_map<int, int> stonePosition;
    int memoization[2001][2001];

    bool canReach(vector<int>& stones) {
        int stoneCount = stones.size();
        // Updating positions of stones in the map
        for (int i = 0; i < stoneCount; i++) {
            stonePosition[stones[i]] = i;
        }
        
        memoization[0][0] = 1;
        for (int idx = 0; idx < stoneCount; idx++) {
            for (int lastJump = 0; lastJump <= stoneCount; lastJump++) {
                if (memoization[idx][lastJump]) {
                    int nextPosition = stones[idx] + lastJump;
                    if (stonePosition.count(nextPosition)) {
                        memoization[stonePosition[nextPosition]][lastJump] = 1;
                    }
                    nextPosition = stones[idx] + lastJump + 1;
                    if (stonePosition.count(nextPosition)) {
                        memoization[stonePosition[nextPosition]][lastJump + 1] = 1;
                    }
                    nextPosition = stones[idx] + lastJump - 1;
                    if (stonePosition.count(nextPosition) && lastJump > 1) {
                        memoization[stonePosition[nextPosition]][lastJump - 1] = 1;
                    }
                }
            }
        }
        
        // Check if reaching the last stone is possible
        for (int lastJump = 0; lastJump <= stoneCount; lastJump++) {
            if (memoization[stoneCount - 1][lastJump]) {
                return true;
            }
        }
        return false;
    }
};

The given problem, known as "Frog Jump," deals with determining whether a frog can jump across various stones positioned at specific points until it reaches the last stone. Implementing this solution in C++, the code utilizes a combination of memoization and hashing (unordered_map) to solve the problem efficiently.

Start by mapping each stone's position to its index with an unordered map, stonePosition. This mapping helps in constant-time look-up for determining if a stone exists at a given position. Next, initialize a 2D array memoization to keep track of reachable positions and jumps.

The main logic involves iterating over each stone and checking if it is possible to jump from the current stone to the next one using last jump's length -1, +0, or +1. If such a jump lands on another stone, that jump is recorded as feasible in the memoization array.

The solution checks all possible jump lengths from each stone and marks them in the 2D memoization array indicating a valid jump from one stone to another.

Finally, to determine if reaching the final stone is possible, iterate through the memoization entries for the last stone. If any entry reveals a feasible jump reaching this stone, then it is possible for the frog to reach the end; otherwise, it isn't.

This method leverages dynamic programming to reduce the complexity of checking every possible sequence of jumps, making it more efficient than a naive approach.

java
class Solution {
    HashMap<Integer,Integer> stoneIndexMap = new HashMap<>();
    boolean pathMatrix[][] = new boolean[2001][2001];

    public boolean canCross(int[] stones) {
        int stoneCount = stones.length;
        for (int i = 0 ; i < stoneCount; i++) {
            stoneIndexMap.put(stones[i], i);
        }

        pathMatrix[0][0] = true;
        for (int index = 0; index < stoneCount; index++) {
            for (int jump = 0; jump <= stoneCount; jump++) {
                if (pathMatrix[index][jump]) {
                    if (stoneIndexMap.containsKey(stones[index] + jump)) {
                        pathMatrix[stoneIndexMap.get(stones[index] + jump)][jump] = true;
                    }
                    if (stoneIndexMap.containsKey(stones[index] + jump + 1)) {
                        pathMatrix[stoneIndexMap.get(stones[index] + jump + 1)][jump + 1] = true;
                    }
                    if (stoneIndexMap.containsKey(stones[index] + jump - 1)) {
                        pathMatrix[stoneIndexMap.get(stones[index] + jump - 1)][jump - 1] = true;
                    }
                }
            }
        }

        for (int i = 0; i <= stoneCount; i++) {
            if (pathMatrix[stoneCount - 1][i]) {
                return true;
            }
        }
        return false;
    }
}

The "Frog Jump" problem involves determining if a frog can hop from the start of an array of stone positions to the end by adhering to specific jumping rules. The solution is implemented in Java, utilizing a dynamic programming approach represented through a 2D boolean matrix (pathMatrix) and a hash map (stoneIndexMap) for quick index referencing of stone positions.

  • stoneIndexMap maps each stone's position to its index in the array, enabling quick look-up.
  • pathMatrix helps track possible positions and jumps. If pathMatrix[index][jump] is true, it indicates that the frog can be on the stone at index with a last jump of jump units.

Key steps in the solution:

  1. Initialize stoneIndexMap with the stone positions and corresponding indices.
  2. Set pathMatrix[0][0] to true, indicating starting at the first stone without any jump.
  3. Iterate through each stone position. For each possible jump length:
    1. If the pathMatrix for the current stone and jump is true, check:
      • The consecutive stone positions (jump, jump+1, and jump-1 units away). Update the corresponding pathMatrix values to true if those stones exist.
  4. Conclude by verifying the last row of the pathMatrix. If any position is true, return true, signifying that the frog can reach the final stone.

This solution leverages memoization to avoid redundant calculations and ensures efficient look-up for possible jumps by using a hash map. The solution dynamically builds up possibilities of reaching each stone with specific jumps, making it efficient for larger stone arrays. Return false if the end of the matrix is reached and no true entry is found in the last row, indicating the frog cannot reach the last stone.

Comments

No comments yet.