最小侧跳次数|刷题打卡

182 阅读3分钟

本文正在参与掘金团队号上线活动,点击 查看大厂春招职位

一、题目描述:

image.png

image.png

image.png

二、思路分析:

今天给大家分享的是力扣第236场周赛的第三题,比赛的时候想了许久,把自己绕进去了都没想明白,赛后看了一下大佬的题解,原来想法差不多,不过有些细节还是想的不对,分享给大家。

贪心解法

比赛的时候我的第一向想法就是直接模拟,下一步没有障碍就直接走,下一步遇到障碍的就选择侧跳更换赛道,然后计数,最后走到最后就可以找到。emmmm,可惜第三个case就没有通过,赛后回顾了一下,发现是当当前道路有障碍的时候,选择侧跳的道路可能有多条,而选择一跳能够沿着其走到最远的位置的道路才是正确的,我当时忽略了掉了这个,所以就直接放弃了模拟的解法。

动态规划

然后就开始换思路了,最少侧跳,emmmm,有点动态规划的意思了。尝试,然后就是状态的定义了,想了一会准备定义一个三维的dp数组,然后dp[i][j][3]表示从位置i跳到位置j的第1,2,3个赛道的最小侧跳数,最后返回dp[0][len_n]中的最小值即可,思考了许久,状态转移方程没搞出来,弃疗了,赛后看大家的DP似乎都没有三维的,看来是我想复杂了。大家普遍都是二维的dp[i][3],表示从开头跳到位置i的某个赛道上的最小侧跳次数。参考了这个题解

  • 状态定义:dp[i][j]表示从开头跳到位置i的某个赛道上的最小侧跳次数,其中j取,0,1,2对应着三个赛道
  • 状态转移方程:当遍历到的位置没有障碍物的时候,当前位置可以直接从前一个位置转移过来;然后还可以从另外两条赛道侧跳过来,此时侧跳数+1;
  • base case: (这里道路我用了1,2,3跟道路对应起来的)dp[0][1] = 1; dp[0][2] = 0; dp[0][3] = 1; 另外在具体代码的实现里有一个地方个人感觉很巧妙,就是选择另外两条道路的时候,通过 % 3 直接求得道路的索引,可以看看原题解的做法,我这里由于使用了1-3的索引,所以求起来稍微有点难看,哈哈哈哈哈。
  • 时间复杂度:O(N),这里 N 是数组的长度。
  • 空间复杂度:O(N)

三、AC 代码:

//错误的解法
class Solution {

    /**
     * @param Integer[] $obstacles
     * @return Integer
     */
    function minSideJumps($obstacles) {
        $len_n = count($obstacles);
        $cur = 2;
        $jump = 0;
        while ($i < $len_n - 1) {
            $curOb = $obstacles[$i];
            $next = $obstacles[$i + 1];
            if ($next == 0 || $next != $cur) {
                $i++;
                continue;
            }
            if ($next == $cur) {
                if ($cur == 1) {
                    if ($curOb == 2) {
                        $cur = 3;
                    } else {
                        $cur = 2;
                    }
                    $jump++;
                    echo "1-->$cur";
                    continue;
                }
                if ($cur == 2) {
                    if ($curOb == 1) {
                        $cur = 3;
                    } else {
                        $cur = 1;
                    }
                    $jump++;
                    echo "2-->$cur";
                    continue;
                }
                if ($cur == 3) {
                    if ($curOb == 1) {
                        $cur = 2;
                    } else {
                        $cur = 1;
                    }
                    $jump++;
                    echo "3-->$cur";
                    continue;
                }
            }
        }
        
        return $jump;
    }
}


//通过的解法
function minSideJumps($obstacles) {
        $len_n = count($obstacles);
        $dp = array_fill(0, $len_n, array_fill(0, 4, PHP_INT_MAX));
        $dp[0][1] = 1;
        $dp[0][2] = 0;
        $dp[0][3] = 1;
        for ($i = 1; $i < $len_n; $i++) {
            for ($road = 1; $road <= 3; $road++) {
                //如果当前道路上有障碍,跳过
                if ($obstacles[$i] == $road) {
                    continue;
                }
                //当前道路上无障碍,从前面直接过来
                $dp[$i][$road] = $dp[$i - 1][$road];
            }
            for ($road = 1; $road <= 3; $road++) {
                //如果当前道路上有障碍,跳过
                if ($obstacles[$i] == $road) {
                    continue;
                }
                //看看另外两条道路怎么样,很巧妙啊这里
                $roadA = ($road + 1) % 3;
                $roadB = ($road + 2) % 3;
                $side = min($dp[$i][$roadA] + 1, $dp[$i][$roadB] + 1);

                $dp[$i][$road] = min($dp[$i][$road], $side);
            }
        }

        return min($dp[$len_n - 1]);
    }

四、总结:

动态规划的关键是定义好一个简单易于理解的状态,然后找出对应的状态转移方程,然后就OK了,注意多练习啊,还是太菜了。。。。。