Dynamic Programming学习笔记 (4) - 跳跃游戏二 (力扣45)

165 阅读2分钟

DP的主要应用领域在于优化和极值问题,下面这个问题就是求最小值的题目。

给定一个非负整数数组 nums,最初位于数组的第一个下标 。

数组中的每个元素代表在该位置时可以跳跃的最大长度。

找到到达数组的最后一个位置所需的最少的跳跃次数。

实例如下,

int[] nums = {3, 1, 2, 0, 4}

答案是2,首先从1号位置跳一次到达3号位置,然后再跳一次从3号位置到达终点5号位置。

使用DP的解题思路如下:

首先,我们定义一个函数F(k)来表示从k号位置出发跳到终点所需的最少次数,使用上面的实例数据,我们可以得到

F(1) = 1 + min(F(2), F(3), F(4))

其中的min()函数返回给的输入的最小值。

这个公式表示我们从1号位置出发跳一次,因为最多可以跳三步所有对应有三个选择,可以跳到2号,3号或4号位置,然后继续再往终点跳,而F(1)的答案就是这三个选项中最小的一个并加上从1号位置出发的一跳。

同理类推,我们可以得到

F(k) =
   0 (k >= N)
   1 + min(F(k + 1), F(k + 2) ... F(k + nums[k])

这里N是nums数组的长度。

使用与跳跃游戏一相同的结构,我们根据数组下标顺序从大到小依次计算DP数组中各个元素的值,所有计算完成后,DP数组第一个元素的数值就是问题的最终答案。

Java 代码如下

class BottomUp {
    public int jump(int[] nums) {
        int N = nums.length;
        if (N == 1) {
            return 0;
        }

        int[] dp = new int[N + 1];
        dp[N - 1] = 0;

        for (int i = N - 2; i >= 0; i --) {
            int jump = nums[i];

            int miniJump = N;
            for (int j = jump; j >= 1; j --) {
                if (i + j >= N) {
                    miniJump = 0;
                    break;
                } else {
                    miniJump = Math.min(miniJump, dp[i + j]);
                }
            }

            dp[i] = 1 + miniJump;
        }

        return dp[0];
    }
}

上面的代码使用双重循环来填充DP数组,其计算复杂度为O(n^2),我们可以做进一步的优化来实现计算复杂度为O(n)的算法。