题解:学而思编程 变种走格子问题

【题目来源】

学而思编程:变种走格子问题

【题目描述】

\(n\) 个格子排成 \(1\) 列,从左至右编号为 \(1\sim n\)。他一开始站在第 \(1\) 个格子上,每次只能向右走若干格。但是小猴一次可以走的格子数 \(x\) 必须满足以下要求:

存在 \(1\le i\le k\),使得 \(x\) 满足 \(L_i\le x\le R_i\)

其中 \(k\) 是一个不超过 \(10\) 的正整数,\([L_1,R_1],[L_2,R_2],\dots,[L_k,R_k]\) 由输入给出。

例如 \(L_1=1,R_1=1,L_2=3,R_2=4\),那么小猴一次走的格子数只能是 \(1,3,4\) 中的某个数。

问:小猴要走到第 \(n\) 格,一共有多少种方法?(输出答案除以 \(998244353\) 的余数)

【输入】

\(1\) 行,\(2\) 个正整数 \(n\)\(k\)

接下来 \(k\) 行,每行 \(2\)个正整数 \(L_i,R_i\)

【输出】

输出方法数除以 \(998244353\) 的余数

【输入样例】

5 2
1 1
3 4

【输出样例】

4

【核心思想】

  1. 问题分析:给定 \(n\) 个格子排成一列,从第 \(1\) 格出发,每次可向右跳跃 \(x\) 格,其中 \(x\) 属于 \(k\) 个给定区间 \([L_i, R_i]\) 的并集。求到达第 \(n\) 格的方法数模 \(998244353\)。这是一个线性动态规划 + 前缀和优化问题,关键在于将多区间跳跃转化为区间求和,并利用前缀和实现 \(O(1)\) 区间查询。

  2. 算法选择

    • 线性 DPdp[i] 表示到达第 \(i\) 格的方法数
    • 前缀和优化:利用前缀和数组 s[i] 将"从多个前驱位置转移"的区间求和优化到 \(O(1)\)
    • 区间容斥:对每个跳跃区间 \([L_i, R_i]\),用前缀和的差分计算合法前驱区间的方案数之和
  3. 关键步骤

    • 初始化
      • 读取 \(n\)(格子数)和 \(k\)(跳跃方式数)
      • 读取 \(k\) 个区间 \([L_i, R_i]\)
      • dp[1] = 1s[1] = 1:从第 \(1\) 格出发有 \(1\) 种方法
    • DP 转移(遍历 \(i\)\(2\)\(n\)):
      • 对每个跳跃区间 \([L_j, R_j]\)\(1 \leq j \leq k\)):
        • 能跳到 \(i\) 的前驱位置范围为 \([i - R_j, i - L_j]\)
        • dp[i] += s[i - L_j]:加上从位置 \(i - L_j\) 及之前的所有方案数(前缀和)
        • dp[i] -= s[i - R_j - 1]:减去从位置 \(i - R_j - 1\) 及之前的方案数,实现区间 \([i - R_j, i - L_j]\) 的精确求和
        • 每次操作后取模
      • s[i] = (s[i - 1] + dp[i]) \bmod MOD:更新前缀和
    • 输出(dp[n] + MOD) \bmod MOD:处理负数后输出
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \times k)\),每个位置遍历 \(k\) 个跳跃区间,每个区间 \(O(1)\) 转移
    • 空间复杂度:\(O(n)\),DP 数组 dp[1..n] 和前缀和数组 s[1..n]
  5. 前缀和区间求和的核心思想

    • 转移方程转化dp[i] = \sum_{j=1}^{k} \sum_{x=L_j}^{R_j} dp[i - x],直接计算为 \(O(n \times k \times \max(R_j - L_j))\)
    • 区间压缩:对于固定区间 \([L_j, R_j]\),内层求和 \(\sum_{x=L_j}^{R_j} dp[i - x] = \sum_{t=i-R_j}^{i-L_j} dp[t] = s[i - L_j] - s[i - R_j - 1]\),降为 \(O(1)\)
    • 前缀和定义s[i] = \sum_{t=1}^{i} dp[t],维护到达每个位置及之前所有位置的方法数前缀和
    • 边界处理i - R_j - 1 \geq 0 时才有意义,否则不减;i - L_j \geq 0 保证前驱位置存在
    • 模运算修正:减法后可能为负,输出时加 MOD 再取模确保结果非负
    • 适用于"多区间跳跃 + 到达计数"类问题,前缀和可将 \(O(n \times k \times V)\) 优化至 \(O(n \times k)\)

【解题思路】

【算法标签】

线性DP-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int MR = 2e5 + 10;
const int MOD = 998244353;
typedef long long LL;
int n, k;
int L[15], R[15];  // L[i], R[i] 表示第i种跳跃的长度范围
int s[MR], dp[MR];  // s是前缀和数组,dp是动态规划数组

int main()
{
    cin >> n >> k;  // 输入总台阶数n和跳跃方式数k
    for (int i = 1; i <= k; i++)
        cin >> L[i] >> R[i];  // 输入每种跳跃的长度范围

    // 初始化
    dp[1] = s[1] = 1;  // 从第1个台阶出发,只有1种方法

    // 动态规划
    for (int i = 2; i <= n; i++) {
        for (int j = 1; j <= k; j++) {
            // 从[i-R[j], i-L[j]]范围内的台阶可以跳到i
            if (i - L[j] >= 0) {
                dp[i] = (dp[i] + s[i - L[j]]) % MOD;
            }
            if (i - R[j] - 1 >= 0) {
                dp[i] = (dp[i] - s[i - R[j] - 1]) % MOD;
            }
        }
        // 更新前缀和
        s[i] = (s[i - 1] + dp[i]) % MOD;
    }

    // 输出结果,处理负数
    cout << (dp[n] + MOD) % MOD << endl;
    return 0;
}

【运行结果】

5 2
1 1
3 4
4
posted @ 2026-07-04 13:18  团爸讲算法  阅读(7)  评论(0)    收藏  举报