LC 1696. 跳跃游戏 VI

1696. 跳跃游戏 VI

难度 : 中等

题目大意:

给你一个下标从 0 开始的整数数组 nums 和一个整数 k

一开始你在下标 0 处。每一步,你最多可以往前跳 k 步,但你不能跳出数组的边界。也就是说,你可以从下标 i 跳到 [i + 1, min(n - 1, i + k)] 包含 两个端点的任意位置。

你的目标是到达数组最后一个位置(下标为 n - 1 ),你的 得分 为经过的所有数字之和。

请你返回你能得到的 最大得分

提示:

  • 1 <= nums.length, k <= 10^5
  • -104 <= nums[i] <= 10^4

示例 1:

输入:nums = [1,-1,-2,4,-7,3], k = 2
输出:7
解释:你可以选择子序列 [1,-1,4,3] (上面加粗的数字),和为 7 。

分析

定义f[i]表示从0~i首尾必须取到的情况下,我们可以得到的最大的数字之和,那么怎么转移呢,我们可以枚举一下从i可以跳到哪里,从对应的状态转移过来即可,简单算一下时间复杂度,枚举下标是 O ( n ) O(n) O(n),还要枚举从哪里跳,时间是 O ( k ) O(k) O(k),那么时间就是 O ( n k ) O(nk) O(nk),会超时,需要考虑优化

记忆化和递推 代码实现

// 记忆化(会超时!!)
class Solution {
public:
    int maxResult(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> f(n, INT_MIN);
        function<int(int)> dfs = [&](int u) -> int{
            if (u == 0) return nums[0];
            int& res = f[u];
            if (res != INT_MIN) return res;
            int t = INT_MIN;
            for (int i = 1; i <= k && u - i >= 0; i ++) {
                t = max(t, dfs(u - i));
            }
            return res = t + nums[u];
        };
        
        return dfs(n - 1);
    }
};

// 递推(会超时!!)
class Solution {
public:
    int maxResult(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> f(n, INT_MIN);
        f[0] = nums[0];
        for (int i = 1; i < n; i ++) {
            int t = INT_MIN;
            for (int j = 1; j <= k && i - j >= 0; j ++) {
                t = max(t, f[i- j]);
            }
            f[i] = t + nums[i];
        }
        return f[n - 1];
    }
};

时间复杂度: O ( n k ) O(nk) O(nk)

优化

我们发现枚举可以跳到哪一步时,我们就是再找从[max(0, i - k - 1), i - 1]这一段区间中找最大值,而且k是固定的,那么怎么快速算出这一段的最大值呢,单调队列!!!,单调队列可以动态求一段区间的最大值或者最小值

单调队列模板

常见模型:找出滑动窗口中的最大值/最小值
int hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{
    while (hh <= tt && check_out(q[hh])) hh ++ ;  // 判断队头是否滑出窗口
    while (hh <= tt && check(q[tt], i)) tt -- ;
    q[ ++ tt] = i;
}

单调队列优化dp

class Solution {
public:
    int maxResult(vector<int>& nums, int k) {
        int n = nums.size();
        int hh = 0, tt = -1;
        int q[n + 1];
        memset(q, 0, sizeof q);
        vector<int> f(n);
        for (int i = 0; i < n; i ++) {
            if (q[hh] < i - k) hh ++;
            f[i] = f[q[hh]] + nums[i];
            while (hh <= tt && f[q[tt]] <= f[i]) tt --;
            q[++ tt] = i;
        }
        return f[n - 1];
    }
};

时间复杂度: O ( n ) O(n) O(n)

结束了

你可能感兴趣的:(算法,leetcode,c++,学习,数据结构,动态规划)