-403. 青蛙过河

题目描述

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

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

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

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

样例

示例 1:
输入:stones = [0,1,3,5,6,8,12,17]
输出:true
解释:青蛙能成功过河,按照如下方案跳跃:跳 1 个单位到第 2 块石子, 然后跳 2 个单位到第 3 块石子, 接着 跳 2 个单位到第 4 块石子, 然后跳 3 个单位到第 6 块石子, 跳 4 个单位到第 7 块石子, 最后,跳 5 个单位到第 8 个石子(即最后一块石子)。

示例 2:
输入:stones = [0,1,2,3,4,8,9,11]
输出:false
解释:这是因为第 5 和第 6 个石子之间的间距太大,没有可选的方案供青蛙跳跃过去。

思路

  • 这题使用一个二维的 dp 求解,其中 dp[ i ] [ j ] 表示第 i 个石子能否通过上一步距离为 j 的跳跃到达。 最后如果有任何 dp [n-1] [x] 为1,表示可以到达终点,否则无法到达终点。
  • 考虑一下这个数组的维度。第一维容易理解,设 stones 数组长度为 n,那么第一维的大小就是 n。对于第二维,两个石子之间的间距可能会非常大,这样的话第二维不是应该非常大吗? 这里有一个技巧,我们知道,青蛙从第 0 个石子跳到 第 1 个石子,长度为1,后面每往后跳一步,跳跃的长度最多只能 +1,所以如果有 n 个石头,其最大跳跃长度也不会超过 n - 1,所以我们第二维大小也开辟为 n。
  • 边界条件:dp [0] [0] = 1,表示在 0个石子处,上一步跳跃为 0 的情况下可以到达。这里第二维设置为 0 是因为从 0 石子到 1 石子的步长固定为 1.
  • 然后用一个两层循环来更新 dp 数组。第一层循环 i 从 0 到 len - 1遍历石子。 第二层循环 j 从 i -1 到 0 遍历可能的前一个石子。我们将石子 i 和石子 j 之间的间距记为 tmp,如果石子 j 能够跳到石子 i,那么只要 dp [ j ] [tmp]、dp [ j ] [tmp-1]、dp [ j ] [tmp+1] 任意一个值为 1就可以了,满足这个条件的话,我们就可以更新 dp [i] [tmp]为 1.
  • 注意,上一步有一个小技巧,在遍历 j 的时候,如果 tmp - 1 > j,那么必然不可能从 j 跳到 i。根据之前的分析,石子 x 的上一步最大跳跃长度不会超过 x,如果 tmp - 1 > j,那么说明 j 石头跳跃 tmp - 1个距离都做不到,更别说跳跃 tmp 或 tmp + 1个距离了,所以这种情况下 j 不能跳到 i。 由于我们是从后往前遍历的 j,所以,接下来的所有 j 都不能跳到 i(因为接下来 j 会越来越小,而 tmp 距离会越来越大),我们可以跳出本轮循环,去进行下一个 i 的循环。
  • 由于两层循环,所以时间复杂度为O ( n^2 )

代码

class Solution {
    public boolean canCross(int[] stones) {
    	 int len = stones.length;
    	 //dp[i][j]表示,第i个石头能否通过上一步长度为j的跳跃到达
    	 int [][] dp = new int [len][len];
    	 //边界条件
    	 dp[0][0] = 1;
    	 
    	 for(int i = 0;i < len;i++) {
    		 //遍历之前的石头
    		 for(int j = i-1;j >= 0;j--) {
    			 //j石头到i石头的距离
    			 int tmp = stones[i] - stones[j];
    			 if(tmp - 1 > j)
    				 break;
    			 if(dp[j][tmp-1] == 1 || dp[j][tmp] == 1 || dp[j][tmp + 1] == 1) {
    				 dp[i][tmp] = 1;
    				 if(i == len-1)
    					 return true;
    			 }
    		 }
    	 }
    	 return false;
    }
    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值