2021-04-29 403. 青蛙过河 [dfs,动态规划]

403. 青蛙过河

思路一:DFS 暴搜

class Solution {
    HashMap<Integer,Integer> map = new HashMap<>();

    // step: 上一步走了多少步
    // idx: 当前石头块编号
    public boolean dfs(int[] stones, int step,int idx){
        if(idx == (stones.length-1)){
            // System.out.print("OK!");
            return true;
        }

        for(int i=-1;i<=1;i++){
            if((step+i)==0) continue; // 如果这次选择原地不动就跳过
            int crtStep = step+i;
            int crtDist = stones[idx]+crtStep;
            if(map.containsKey(crtDist) && dfs(stones,crtStep,map.get(crtDist)))
                return true;
        }
        return false;
    }

    public boolean canCross(int[] stones) {
        if((stones[1]-stones[0])!=1) return false;

        // 构建一个距离 -> stones index的HashMap
        for(int i=0;i<stones.length;i++)
            map.put(stones[i],i);

        return dfs(stones,1,1);
    }
}

思路二:记忆化

在考虑加入「记忆化」时,我们只需要将 DFS 方法签名中的【可变】参数作为维度,DFS 方法中的返回值作为存储值即可。

class Solution {
    HashMap<Integer,Integer> map = new HashMap<>();
    Map<String, Boolean> cache = new HashMap<>(); // 用于记忆画存储

    // step: 上一步走了多少步
    // idx: 当前石头块编号
    // 我们这里每步的String格式是"idx_step"
    public boolean dfs(int[] stones, int step,int idx){
        String tmp = idx+"_"+step;
        // 如果之前遇到过
        if(cache.containsKey(tmp))  return cache.get(tmp);

        if(idx == (stones.length-1)){
            // System.out.print("OK!");
            return true;
        }
        for(int i=-1;i<=1;i++){
            if((step+i)==0) continue; // 如果这次选择原地不动就跳过
            int crtStep = step+i;
            int crtDist = stones[idx]+crtStep;
            if(map.containsKey(crtDist)){
                boolean res = dfs(stones,crtStep,map.get(crtDist));
                cache.put(tmp,res);
                if(res) return true;
            }
        }

        cache.put(tmp,false); // 因为这种情况下不管这一步走多少都不行
        return false;
    }

    public boolean canCross(int[] stones) {
        if((stones[1]-stones[0])!=1) return false;

        // 构建一个距离 -> stones index的HashMap
        for(int i=0;i<stones.length;i++)
            map.put(stones[i],i);

        return dfs(stones,1,1);
    }
}

思路三:动态规划

因此可以设定为 f[][]作为动规数组:

  • 第一维为可变参数 u,代表当前处于的石子列表的下标,范围为数组 stones 长度;
  • 第二维为可变参数 k,代表上一步的的跳跃步长,前面也分析过了,最多不超过数组 stones 长度。
class Solution {
    public boolean canCross(int[] ss) {
        int n = ss.length;
        // check first step
        if (ss[1] != 1) return false;
        boolean[][] f = new boolean[n + 1][n + 1];
        f[1][1] = true;
        for (int i = 2; i < n; i++) {
            for (int j = 1; j < i; j++) {
                int k = ss[i] - ss[j];
                // 我们知道从位置 j 到位置 i 是需要步长为 k 的跳跃

                // 而从位置 j 发起的跳跃最多不超过 j + 1
                // 因为每次跳跃,下标至少增加 1,而步长最多增加 1 
                if (k <= j + 1) {
                	// 来i之前是在j跳了k步,则j之前一个节点到达j走的步数只可能是k-1,k,k+1。
                    f[i][k] = f[j][k - 1] || f[j][k] || f[j][k + 1];   
                }
            }
        }
        for (int i = 1; i < n; i++) {
            if (f[n - 1][i]) return true;
        }
        return false;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值