前言
动态规划(DP)是算法学习中的核心难点,也是竞赛和面试的必考点。线性 DP 作为最基础的分支,状态转移具有明显的线性关系,逻辑清晰,非常适合作为入门切入点。
本文带大家吃透基础线性 DP,从核心思想拆解到 4 道经典例题,再到空间优化技巧。
一、线性 DP 核心思想:把复杂问题'线性化'
1.1 线性 DP 的定义
线性 DP 是动态规划的一种特殊形式,其核心特点是状态之间的转移关系呈线性结构。简单来说,DP 状态可以用一维或二维数组表示,推导顺序是线性的——要么从左到右、要么从上到下。
例如一维线性 DP 中,dp[i] 的取值只依赖 dp[i-1]、dp[i-2] 等前面的状态;二维线性 DP 中,dp[i][j] 也只依赖相邻的线性状态。
1.2 线性 DP 解题四步走
解决任何线性 DP 问题,都可以遵循这四个核心步骤:
- 状态表示:定义 DP 数组的含义。
- 状态转移方程:推导当前状态如何由前面的状态计算得出。
- 初始化:确定 DP 数组的初始值。
- 填表顺序:根据状态转移方程,确定 DP 数组的计算顺序。
1.3 线性 DP 的优势
相比于其他 DP 分支,线性 DP 的优势在于逻辑清晰、代码简洁、实用性强,很多实际问题(如路径计数、最值问题)都可以抽象为线性 DP 模型。
二、经典例题实战:从易到难吃透基础线性 DP
例题 1:台阶问题(洛谷 P1192)—— 一维线性 DP 入门
题目描述
有 N 级台阶,一开始在底部,每次可以向上迈 1~K 级台阶,问到达第 N 级台阶有多少种不同方式?结果对 100003 取模。 输入:两个正整数 N, K(1≤N≤1e5,1≤K≤100) 输出:到达第 N 级台阶的不同方式数(mod 100003)
思路拆解
- 状态表示:定义
dp[i]表示'到达第 i 级台阶的不同方式数'。 - 状态转移方程:要到达第 i 级,最后一步可以从
i-1到i-K级迈过来。dp[i] = (dp[i-1] + dp[i-2] + ... + dp[i-K]) % MOD - 初始化:
dp[0] = 1。 - 填表顺序:从左到右依次计算
dp[1]到dp[N]。
代码实现(基础版)
#include <iostream>
using namespace std;
const int N = 1e5 + 10, MOD = 1e5 + 3;
int n, k;
int dp[N];
{
cin >> n >> k;
dp[] = ;
( i = ; i <= n; i++) {
( j = ; j <= k && i - j >= ; j++) {
dp[i] = (dp[i] + dp[i - j]) % MOD;
}
}
cout << dp[n] << endl;
;
}


