Leetcode-403-青蛙过河(DFS+备忘录)

问题描述

思路简述

这道题标签是DP类题目,但是用DFS+记忆剪枝也可以解决,DFS的思路是模拟暴搜所有可能的跳跃方案来判断是否有成功的方案,设计DFS函数的时候考虑当前石头的下标(用于判断是否到达了最后一块石头)以及上一步所用步数(用于枚举计算下一步能够跳出的可能步数);

下面先给出DFS不带记忆的代码:

class Solution {
public:
    map<int, int> s_map;
    bool canCross(vector<int>& stones) {
        // 将石头下标存放在map中,方便判断某位置是否有石头以及该石头的下标
        for(int i=0; i<stones.size(); i++) s_map[stones[i]] = i;
        
        // 由题意可知,初始时在第一块石头0上,并且第一次只能跳一步,所以如果1不存在,那就直接不可能有可行的方案了
        if(s_map.count(1) == 0) return false;

        
        return dfs(stones, 1, 1);
    }

    // cur_idx:当前所处的石头下标
    // k:上一步跳到这一步所用的步数,用于计算下一步跳跃的可能性
    bool dfs(vector<int> stones, int cur_idx, int k){
         // 当前坐标为最后一块石头,即方案可行
         if(cur_idx == stones.size()-1) return true;

         // 遍历-1,0,1,也就是遍历下一步能够跳跃的范围
         for(int i=-1; i<=1; i++){
             int next = stones[cur_idx] + k + i;

             // 原地踏步的情况
             if(next == stones[cur_idx]) continue;

             // 当前的跳跃方案处有石头,则在这种情况下继续DFS
             if(s_map.count(next) > 0){
                 bool flag = dfs(stones, s_map[next], k+i);
                 if(flag) return true;
             }
         }
         return false;
     }
};

不加记忆的DFS在第十五个用例处就会出现超时,于是考虑备忘录搜索,备忘录需要记的内容为:[某坐标,上次跳跃步数]的条件下能否到达最后一块石头,可以考虑用bool型,但是bool型只能存储是否能达到,不能存储当前情况是否在备忘录上,于是可以用能够int型的数组,即:int memo[index][last_jump]这样的数组,但是如果用数组的话考虑到可能会开百万级别大小的数组空间,于是可以用哈希表,哈希表的key为由index和last_jump组成的字符串,value为true或false,如果该key不存在则认为是还没有搜索过的场景。

class Solution {
public:
    map<int, int> s_map;
    map<string, bool> memo; // 加入备忘录
    bool canCross(vector<int>& stones) {
        for(int i=0; i<stones.size(); i++) s_map[stones[i]] = i;
        if(s_map.count(1) == 0) return false;

        
        return dfs(stones, 1, 1);
    }
     bool dfs(vector<int> stones, int cur_idx, int k){
         // 备忘录的key为cur_index与k组成的字符串
         string key = to_string(cur_idx) + "_" + to_string(k);

         // 如果memo中存在,则认为已经计算过的情况,则直接返回备忘录的记录
         if(memo.count(key) > 0) return memo[key];

         if(cur_idx == stones.size()-1) return true;
         for(int i=-1; i<=1; i++){
             int next = stones[cur_idx] + k + i;
             if(next == stones[cur_idx]) continue;
             if(s_map.count(next) > 0){
                 bool flag = dfs(stones, s_map[next], k+i);
                 
                 // 将当前情况(key)以及对应的解决方案(flag)存入备忘录(memo)中 
                 memo[key] = flag;

                 if(flag) return true;
             }
         }
         return false;
     }
};

虽然记忆化搜索后能够AC了,但是时间复杂度还是有点高。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值