青蛙过河 | Java刷题打卡

539 阅读2分钟

本文正在参加「Java主题月 - Java 刷题打卡」,详情查看 活动链接

一、题目描述:

image.png

二、思路分析:

今天给大家分享的是一道HARD难度的题目。 题目的意思是河流被分成了好多单元格,每个单元格有可能放石头,只有踩着石头才能过河。给一个石头列表,青蛙一开始在第一块石头上。第一次只能跳1步。之后可以跳跃多少步取决于上一步跳了多远。如果上次跳了K步,那么接下来只能跳跃K-1,K,K+1步。问问青蛙最后能不能过河,即到达最后一块石头上。青蛙只能往前走哦。

  • 拿到题目,一看每次到一个位置都有三个选择,k-1,k,k+1。我的第一思路是BFS,因为之前看过labuladong的一些类似的题目分析,有了这么个印象。
  • 具体实现呢就是使用一个队列保存当前可能跳跃到的位置,然后从队列里取出位置开始往下跳。如果取出来的位置恰好是我们的目的地,那么直接返回true即可。否则就开始下次跳跃。
  • 而下次要跳多少步,需要知道上一步是怎么过来的,所以需要记录一个K,对应到当前位置是跳了几步来的,使用额外的数组记录一下。
  • 直接提交,不出意外,超时了。
  • 想一下有没有可以优化的地方。
  • 优化: 可以记录一下青蛙跳跃过的路径情况,比如说跳了K步跳到某个石头上,后面再有别的选择跳到这个石头上,可以直接忽略,算是一波剪枝了。

image.png

三、AC 代码:

//未剪枝,超时
class Solution {

    /**
     * @param Integer[] $stones
     * @return Boolean
     */
    function canCross($stones) {
        $len_n = count($stones);
        $dest = $stones[$len_n - 1];
        $map = [];
        for ($i = 0; $i < $len_n; $i++) {
            $map[$stones[$i]] = 1;
        }
        $queue = new SplQueue();
        $arrK = [];
        $queue->enqueue($stones[0]);
        array_push($arrK, 1);
        $first = true;
        while (!$queue->isEmpty()) {
            $size = count($queue);

            for ($i = 0; $i < $size; $i++) {
                $curK = array_shift($arrK);
                if ($first) {
                    $nextSteps = [$curK];
                    $first = false;
                } else {
                    $nextSteps = [$curK, $curK - 1, $curK + 1];
                }
                $curStone = $queue->dequeue();
                if ($curStone == $dest) {
                    return true;
                }
                foreach ($nextSteps as $nextStep) {
                    if ($nextStep == 0) {
                        continue;
                    }
                    if (isset($map[$nextStep + $curStone])) {
                        $queue->enqueue($nextStep + $curStone);
                        array_push($arrK, $nextStep);
                    }
                }
            }
        }

        return false;
    }
}

//通过
class Solution {

    /**
     * @param Integer[] $stones
     * @return Boolean
     */
    function canCross($stones) {
        $len_n = count($stones);
        $dest = $stones[$len_n - 1];
        $map = [];
        $pathMap = [];
        for ($i = 0; $i < $len_n; $i++) {
            $map[$stones[$i]] = 1;
        }
        $queue = new SplQueue();
        $arrK = [];
        $queue->enqueue($stones[0]);
        array_push($arrK, 1);
        $first = true;
        while (!$queue->isEmpty()) {
            $size = count($queue);

            for ($i = 0; $i < $size; $i++) {
                $curK = array_shift($arrK);
                if ($first) {
                    $nextSteps = [$curK];
                    $first = false;
                } else {
                    $nextSteps = [$curK, $curK - 1, $curK + 1];
                }
                $curStone = $queue->dequeue();
                if ($curStone == $dest) {
                    return true;
                }
                foreach ($nextSteps as $nextStep) {
                    if ($nextStep == 0 || isset($pathMap["{$curStone}_{$nextStep}"])) {
                        continue;
                    }
                    if (isset($map[$nextStep + $curStone])) {
                        $pathMap["{$curStone}_{$nextStep}"] = 1;
                        $queue->enqueue($nextStep + $curStone);
                        array_push($arrK, $nextStep);
                    }
                }
            }
        }

        return false;
    }
}

四、总结:

一般看到这种做选择的问题,可以考虑一下使用BFS暴力搞一下,要是超时的话可以尝试剪枝。实在不行的话只能再考虑别的方法了,比如动态规划。