本文正在参加「Java主题月 - Java 刷题打卡」,详情查看 活动链接
一、题目描述:
二、思路分析:
今天给大家分享的是一道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];
}
四、总结:
首先看到这道题 有几种选择的时候 我们应该往回溯思想上靠 先提交验证一下自己的思路是否正确 如果提交发现超时后 我们就需要换用动态规划算法来处理了 这应该是一种常见的思路 希望大家可以记一下~