LeetCode 403. 青蛙过河

这是一篇关于LeetCode 403题目的解析,难度为困难。青蛙过河的问题是一个动态规划问题,需要判断青蛙能否从起点跳跃到终点。关键在于理解,当前位置的可达性不仅取决于前一个位置,还可能涉及中间的石子,且跳跃有3种选择。错误分析和解决方案也被详细讨论,包括如何设计dp状态转移的优化技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

403. 青蛙过河

难度:困难
语言:java

题目内容

一只青蛙想要过河。 假定河流被等分为若干个单元格,并且在每一个单元格内都有可能放有一块石子(也有可能没有)。 青蛙可以跳上石子,但是不可以跳入水中。

给你石子的位置列表 stones(用单元格序号 升序 表示), 请判定青蛙能否成功过河(即能否在最后一步跳至最后一块石子上)。

开始时, 青蛙默认已站在第一块石子上,并可以假定它第一步只能跳跃一个单位(即只能从单元格 1 跳至单元格 2 )。

如果青蛙上一步跳跃了 k 个单位,那么它接下来的跳跃距离只能选择为 k - 1、k 或 k + 1 个单位。 另请注意,青蛙只能向前方(终点的方向)跳跃。
在这里插入图片描述

题目分析

困难题,我没有看答案先自己判断了一下,感觉就是一个dp的题目,能否跳到最后一个,取决于,前面能否有一个满足要求的跳点。
但是设计dp的时候,要考虑以下两种情况:

  1. 能否跳到当前位置并不是直接由前一个决定(可能跨越了中间的一部分石子)
  2. 可能有多种情况可以跳跃到当前位置(因为有三种跳跃距离的选择)

设计的dp函数我觉得要包括的点一定是,从上一步跳过来用了多少步,不然没法考虑下面的跳跃长度,所以设计dp 的List包含两项是很容易想到的,一项是代表位置,一项是代表跳跃步数,但是比较巧妙的一个点是,用Boolean型的值,作为dp里面的Value,可以方便求得下面的值

在这里dp[i][k]里的值代表在第j个点,要跳k步才能到达第i个点,
只有跳到j点时,跳了k-1,k,k+1步才能得到k这个选项,
也就是后面三个值,至少有一个是true,是个或的关系

dp[i][k] = dp[j][k-1] || dp[j][k] || dp[j][k+1];

记录一下几个错误
开始的时候我很快就写起来了
这里面有我的两个错误,通过注释标注出来了

class Solution {
    public boolean canCross(int[] stones) {
      int n = stones.length;
      boolean [][] dp = new boolean[n][stones[n-1]+2]; //用例还考虑了int的边界值的情况
      dp[0][0] = true;
      for (int i =1;i<n;i ++){
          for (int j = i-1;j>=0;j--){
              int k = stones[i] -stones[j];
              dp[i][k] = dp[j][k-1] || dp[j][k] || dp[j][k+1];
              if(i==n-1){
                return dp[i][k]; //这里如果没有把dp的结果放进去,在第一次到达N-1的时候就不会遍历j了
              }
          }
        }
        return false;  
    }
}

好吧,时间原因,再次借鉴了答案,发现了一下设计中的细节。

  1. 「现在所处的石子编号」为 i 时,「上一次跳跃距离」k 必定满足 k <=i。
    这是因为一次跳跃的距离最多就加1嘛,每次都是+1,也只能跟i相同。
  2. 当第 i 个石子与第 i-1个石子距离超过 i时,青蛙必定无法到达终点。
    因为上面1里面的原因,这一点也就很好理解了

于是再次进行了一些优化,得到了答案的代码

class Solution {
    public boolean canCross(int[] stones) {
        int n = stones.length;
        boolean[][] dp = new boolean[n][n]; 
        dp[0][0] = true;
        for (int i = 1; i < n; ++i) {
            if (stones[i] - stones[i - 1] > i) {
                return false; 
               	//这里就是上文的细节2
            }
        }
        for (int i = 1; i < n; ++i) {
            for (int j = i - 1; j >= 0; --j) {
                int k = stones[i] - stones[j];
                if (k > j + 1) {
                    break; //这是上文的细节1,也是由于这个存在,dp的第二项只需要设为长度n即可了
                }
                dp[i][k] = dp[j][k - 1] || dp[j][k] || dp[j][k + 1];
                if (i == n - 1 && dp[i][k]) {
                // 这里与上面代码的比较,需要加上判断条件而不是直接输出
                    return true; 
                }
            }
        }
        return false;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值