动态规划--每日一练(线性DP:LIS的变形+滑动窗口)

P1725 琪露诺 

目录

1.题目描述

2.解题思路

1. LIS 模型与本题的联系

2. 为什么可以看作 LIS 变种?

3. 本题能够清楚的说明动态规划的本质:

4. 本题的结果计算有别于普通DP:

5.本题的优化思想:滑动窗口指路-->优化技巧--滑动窗口-CSDN博客

3.代码展示

暴力做法(会超时)

单调队列法(最优解法)


1.题目描述

在幻想乡,琪露诺是以笨蛋闻名的冰之妖精。

某一天,琪露诺又在玩速冻青蛙,就是用冰把青蛙瞬间冻起来。但是这只青蛙比以往的要聪明许多,在琪露诺来之前就已经跑到了河的对岸。于是琪露诺决定到河岸去追青蛙。

小河可以看作一列格子依次编号为 0 到 N,琪露诺只能从编号小的格子移动到编号大的格子。而且琪露诺按照一种特殊的方式进行移动,当她在格子 i 时,她只移动到区间 [i+L,i+R] 中的任意一格。你问为什么她这么移动,这还不简单,因为她是笨蛋啊。

每一个格子都有一个冰冻指数 Ai​,编号为 0 的格子冰冻指数为 0。当琪露诺停留在那一格时就可以得到那一格的冰冻指数 Ai​。琪露诺希望能够在到达对岸时,获取最大的冰冻指数,这样她才能狠狠地教训那只青蛙。

但是由于她实在是太笨了,所以她决定拜托你帮它决定怎样前进。

开始时,琪露诺在编号 0 的格子上,只要她下一步的位置编号大于 N 就算到达对岸。

输入格式

第一行三个正整数 N,L,R。

第二行共 N+1 个整数,第 i 个数表示编号为 i−1 的格子的冰冻指数 Ai−1​。

输出格式

一个整数,表示最大冰冻指数。

输入输出样例

输入

5 2 3
0 12 3 11 7 -2

输出 

11

说明/提示

对于 60% 的数据,N≤104。

对于 100% 的数据,N≤2×105,−103≤Ai​≤103,1≤L≤R≤N。数据保证最终答案不超过 231−1。

2.解题思路

状态表示:dp[ i ]表示到达第 i  个格子所得的最大冰冻指数。

状态计算(计算每一个格子的最大冰冻指数)

 dp[ i ] = max ( dp[ i ] , dp[ j ] + a[ i ] ) , j ∈ [i-R, i-L]。

1. LIS 模型与本题的联系

1. 核心思想:状态转移依赖于前序元素

  • 传统 LIS:在序列中找到最长的递增子序列,状态转移方程为:
    dp[i] = max(dp[j] + 1),其中 j < i 且 a[j] < a[i]
    每个元素 i 的状态依赖于前面比它小的元素 j

  • 本题:在格子中找到最大冰冻指数路径,状态转移方程为:
    dp[i] = max(dp[j] + A[i]),其中 j ∈ [i-R, i-L] 且 j ≥ 0
    每个格子 i 的状态依赖于前面区间 [i-R, i-L] 内的格子 j

2. 序列约束:元素必须按顺序选择

  • LIS:子序列中的元素必须保持原序列的顺序(但不要求连续)。
  • 本题:跳跃方向必须从编号小的格子到编号大的格子(i > j)。

2. 为什么可以看作 LIS 变种?

1. 问题结构相似

两者都属于序列上的动态规划问题,状态转移都依赖于前序元素的状态。

2. 变种点在于 “可达条件”

  • LIS 的可达条件是 a[j] < a[i],而本题的可达条件是 j ∈ [i-R, i-L]
  • 本质上都是在满足特定条件的前序元素中选择最优解

3. 本题能够清楚的说明动态规划的本质:

1.动归的核心思想:

        动态规划的核心思想正是通过预先计算每个状态的值,避免重复计算,从而在最终求解时直接利用这些状态值进行快速决策。

2.状态计算:计算每个状态值

  • 状态 dp[i] 表示到达格子 i 时的最大冰冻指数,它是前序状态(dp[j], j∈[i-R,i-L])的函数。
  • 状态转移方程 dp[i] = max(dp[j] + A[i]) 体现了通过前序状态推导当前状态的逻辑。
  • 存储 dp 数组 避免了对每个 i 重复计算前序状态,直接复用已计算的 dp[j]

3.结果计算:直接复用状态值

        最终求解时,无需重新计算每个起跳点 i 的冰冻指数,而是直接从 dp 数组中读取 dp[i] 的值,并比较得出最大值。这体现了动态规划的核心优势:通过预处理状态值,将最终决策转化为简单的查表和比较操作

4. 本题的结果计算有别于普通DP:

结果计算需要全局筛选:
        普通 DP 的结果通常是 DP 表的 “终点”(如dp[n])或全局最大值,而冰冻题需要从所有可达终点的状态中筛选出满足条件的最优解。最终答案是所有能一步跳出终点的位置 i 中,dp[i] 的最大值。⚠️⚠️⚠️

5.本题的优化思想:滑动窗口指路-->优化技巧--滑动窗口-CSDN博客

  • 用 dp[i] 表示到达第 (i) 格子的最大冰冻指数。
  • 枚举 (i) 从 1 到 (N),对于每个 (i),要找到区间 ([i-R, i-L]) 内所有的 (dp[j]) 的最大值(即上一步能跳到 (i) 的所有 (j))。
  • 用单调队列维护这个区间最大值。

3.代码展示

暴力做法(会超时)

#include 
#include 
#include 
using namespace std;

int main() {
    int N, L, R;
    cin >> N >> L >> R;
    vector A(N + 1);
    for (int i = 0; i <= N; ++i) cin >> A[i];

    vector dp(N + 1, -1e9);
    dp[0] = 0;

    for (int i = 1; i <= N; ++i) {
        for (int j = i - R; j <= i - L; ++j) {  //依赖于前几个状态
            if (j >= 0 && dp[j] != -1e9) {      //注意可达性检测⚠️⚠️⚠️
                dp[i] = max(dp[i], dp[j] + A[i]);
            }
        }
    }

    int ans = -1e9;
    for (int i = 0; i <= N; ++i) {
        if (dp[i] == -1e9) continue;
        // 只需判断i能否一步跳出终点即可
        if (i + L > N) ans = max(ans, dp[i]);
        else if (i + R > N) ans = max(ans, dp[i]);
    }
    cout << ans << endl;
    return 0;
}

单调队列法(最优解法)

手搓一个例子:

  • 数组长度:N = 6
  • 跳跃步长范围:L = 2, R = 4
  • 窗口大小:R - L + 1 = 3
  • 冰冻指数数组:A = [0, 5, -3, -5, -2, 4, 3](下标从 0 开始) 

动态规划--每日一练(线性DP:LIS的变形+滑动窗口)_第1张图片

动态规划--每日一练(线性DP:LIS的变形+滑动窗口)_第2张图片

#include 
#include 
#include 
#include 
using namespace std;

int main() {
    int N, L, R;
    cin >> N >> L >> R;
    vector A(N + 1);
    for (int i = 0; i <= N; ++i) cin >> A[i];

    vector dp(N + 1, -1e9); // -inf
    dp[0] = 0;
    
    deque q; // 单调队列,维护窗口[i-R, i-L]最大dp

    for (int i = 1; i <= N; ++i) {
        // 右端点:i-L,i-L>=0才可能是前驱
        if (i - L >= 0) {
            // 弹出队尾所有比当前dp小的
            while (!q.empty() && dp[q.back()] <= dp[i - L])
                q.pop_back();
            q.push_back(i - L);
        }
        // 左端点:i-R
        while (!q.empty() && q.front() < i - R)
            q.pop_front();

        if (!q.empty())
            dp[i] = dp[q.front()] + A[i];
        // 若队列空,则说明不可达,dp[i]=-inf
    }

    int ans = -1e9;
    // 枚举所有可一步跳出终点的格子
    for (int i = 0; i <= N; ++i) {
        if (dp[i] == -1e9) continue;
        // 只需判断i能否一步跳出终点即可
//最终答案是所有能一步跳出终点的位置 i 中,dp[i] 的最大值。⚠️⚠️⚠️
        if (i + L > N) ans = max(ans, dp[i]);
        else if (i + R > N) ans = max(ans, dp[i]);⚠️⚠️⚠️
    }
    cout << ans << endl;
    return 0;
}

你可能感兴趣的:(动态规划,动态规划,算法)