停在原地的方案数 | Java刷题打卡

220 阅读2分钟

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

一、题目描述:

image.png

image.png

二、思路分析:

今天给大家分享的是一道HARD题目,给定一个数组,有一个指针一开始位于索引0,然后你可以进行一些操作,将指针左移一步,右移一步或者待在院队不动。问给你一个起始步数,移动完后恰好回到索引0的方案数。注意指针不能越界。 乍一看跟昨天分享的题目是不是有点类似?也是做选择,每次都有三个选择,可以向左,向右,停在原地。我也是这么想的,所以我上来就是一顿回溯。

遍历回溯做选择

  • 定义递归函数,为当前剩余steps 步时,当前停在位置 curPos, 能不能回到原点。
  • 具体可以看代码里的注释。
  • 不出意外,啪的一下超时了,挂在第12个case上。

上dp

  • 既然超时了,说明我们的思路是正确的。那么此时按照经验我们需要往动态规划的思路上靠近了。
  • 可以定义dp数组,dp[i][j],表示还剩i步时,位于位置j时的方案数。
  • 那么对于每个dp[i][j],他有三种状态转移过来,待在原地,向左和向右。
  • 待在原地的时候,只是上一步步数减1,位置不变,所以此时dp[i][j] = dp[i+1][j]
  • 上一步左移过来的时候, dp[i][j] = dp[i + 1][j - 1],此时注意j的位置
  • 上一步右移过来的时候,dp[i][j] = dp[i + 1][j + 1],此时注意j的位置
  • 总的方案数就是上面三种状态转移相加之和
  • 最后我们要求的是dp[0][0],即剩余0步时 位于位置0的方案数。

三、AC 代码:

//超时了
class Solution {

    /**
     * @param Integer $steps
     * @param Integer $arrLen
     * @return Integer
     */
    protected $res = 0;
    function numWays($steps, $arrLen) {
        $this->help($steps, $arrLen, 0, []);
        return $this->res;
    }

    //剩余步数、数组长度 得减1、当前位置从0开始
    protected function help($steps, $arrLen, $curPos, $path)
    {
        if ($steps == 0) {
            if ($curPos == 0) {
                $this->res ++;
                //var_dump($path);
            }
            return;
        }
        //每次可选择左移、不动、右移
        for ($i = -1; $i < 2; $i++) {
            if ($curPos + $i >= $arrLen || $curPos + $i < 0) {
                continue;
            }
            $path[] = $curPos + $i;
            $this->help($steps - 1, $arrLen, $curPos + $i, $path);
            array_pop($path);
        }

        return;
    }
}


//dp解法
function numWays($steps, $arrLen) {
        $mod = 1000000007;
        $max = min((int)($steps / 2), $arrLen - 1);
        //在位置j,剩余步数i的时候的方案数
        $dp = array_fill(0, $steps + 1, array_fill(0, $max + 1, 0));
        $dp[$steps][0] = 1;
        for ($i = $steps - 1; $i >= 0; $i--) {
            for ($j = 0; $j <= $max; $j++) {
                //原地不动,步数减1。从i+1步减过来
                $dp[$i][$j] += $dp[$i + 1][$j];
                //从左边移动过来
                if ($j >= 1) {
                    $dp[$i][$j] += $dp[$i + 1][$j - 1] ;
                }
                //从右边移动过来
                if ($j + 1 <= $max) {
                    $dp[$i][$j] += $dp[$i + 1][$j + 1] ;
                }

                $dp[$i][$j] = $dp[$i][$j] % $mod;
            }
        }

        return $dp[0][0];
    }


四、总结:

首先看到这道题 有几种选择的时候 我们应该往回溯思想上靠 先提交验证一下自己的思路是否正确 如果提交发现超时后 我们就需要换用动态规划算法来处理了 这应该是一种常见的思路 希望大家可以记一下~