leetcode
leetcode 2151 ~ 2200
青蛙过河 II

青蛙过河 II

难度:

标签:

题目描述

You are given a 0-indexed integer array stones sorted in strictly increasing order representing the positions of stones in a river.

A frog, initially on the first stone, wants to travel to the last stone and then return to the first stone. However, it can jump to any stone at most once.

The length of a jump is the absolute difference between the position of the stone the frog is currently on and the position of the stone to which the frog jumps.

  • More formally, if the frog is at stones[i] and is jumping to stones[j], the length of the jump is |stones[i] - stones[j]|.

The cost of a path is the maximum length of a jump among all jumps in the path.

Return the minimum cost of a path for the frog.

 

Example 1:

Input: stones = [0,2,5,6,7]
Output: 5
Explanation: The above figure represents one of the optimal paths the frog can take.
The cost of this path is 5, which is the maximum length of a jump.
Since it is not possible to achieve a cost of less than 5, we return it.

Example 2:

Input: stones = [0,3,9]
Output: 9
Explanation: 
The frog can jump directly to the last stone and come back to the first stone. 
In this case, the length of each jump will be 9. The cost for the path will be max(9, 9) = 9.
It can be shown that this is the minimum achievable cost.

 

Constraints:

  • 2 <= stones.length <= 105
  • 0 <= stones[i] <= 109
  • stones[0] == 0
  • stones is sorted in a strictly increasing order.

代码结果

运行时间: 62 ms, 内存: 30.8 MB


/* 
思路:
1. 使用二分查找来找到最小代价。
2. 通过贪心策略检查某个代价是否可行。
3. 通过Java Stream简化代码逻辑。
*/

import java.util.Arrays;

public class Solution {
    public int minCost(int[] stones) {
        int left = 0, right = stones[stones.length - 1];
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (canCross(stones, mid)) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

    private boolean canCross(int[] stones, int maxJump) {
        int[] visited = new int[stones.length];
        return dfs(stones, maxJump, 0, visited);
    }

    private boolean dfs(int[] stones, int maxJump, int index, int[] visited) {
        if (index == stones.length - 1) return true;
        visited[index] = 1;
        return Arrays.stream(stones)
            .skip(index + 1)
            .takeWhile(i -> stones[i] - stones[index] <= maxJump)
            .anyMatch(i -> visited[i] == 0 && dfs(stones, maxJump, i, visited));
    }
}

解释

方法:

题解的核心思想是计算青蛙从第一块石头到最后一块石头并返回第一块石头的过程中遇到的最大跳跃长度。首先计算从第一块石头到第二块石头的跳跃距离,然后从第二块石头开始,计算从当前石头跳到前一块石头的距离,并用一个变量维护遇到的最大跳跃距离。这种方法确保了每次跳跃的长度是连续的两块石头之间的距离,并且通过遍历数组一次来确定最大的跳跃长度。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
题解中提到'计算从当前石头跳到前一块石头的距离',但代码实现似乎是计算每两块石头间的距离,请问这是否是一个错误?
这确实是一个错误。题解中的描述与代码实现不一致。代码实际上是计算每两块石头间的距离(例如,从第 0 块石头到第 2 块,从第 1 块到第 3 块等),而不是每次从当前石头跳到前一块石头的距离。正确的实现应该是计算从一个石头到下一个石头的距离,并且,最后从最后一个石头跳回第一个石头的距离也应该被考虑进去,以确保找到真正的最大跳跃长度。
🦆
算法在计算最大跳跃距离时,为何没有考虑从最后一块石头跳回第一块石头的距离?
不考虑从最后一块石头跳回第一块石头的距离是一个遗漏。根据题目要求,青蛙需要从第一块石头跳到最后一块石头,然后再返回到第一块石头。因此,正确的实现应该包括计算并比较从最后一块石头跳回第一块石头的距离,这可能是整个路径中的最大跳跃距离。忽略这一点会导致算法不能正确地找到实际的最大跳跃长度。
🦆
题解中提到'通过遍历数组一次来确定最大的跳跃长度',能否详细解释这种一次遍历的策略如何确保找到确切的最大跳跃长度?
这种一次遍历策略基于假设青蛙每次只能跳到相邻的石头或者跳过一个石头。在这种情况下,通过一次遍历数组,比较每次跳跃的长度(即相邻两块或相隔一块石头的距离),可以确保找到最大跳跃长度。这是因为数组中石头的位置是严格递增的,任何更长的跳跃都涉及更远的石头,其跳跃长度必然不会少于在它之前的任何较短跳跃。然而,这种策略还应该包括从最后一块石头跳到第一块石头的距离,以确保覆盖所有可能的跳跃情况。

相关问题