动态规划专题(00):线性动态规划

一、线性动态规划的定义

具有线性阶段划分的动态规划算法称为线性动态规划(简称线性DP)。若状态包含多个维度,则每个维度都是线性划分的阶段,也属于线性DP

1. 核心概念解读

  • 动态规划(DP):是一种解决复杂问题的高效算法思想,其核心是将原问题分解为相对简单的子问题,并通过保存子问题的解(即“状态”)来避免重复计算,最终获得原问题的解。

  • “线性”的含义:这里的“线性”特指问题求解过程在阶段划分上是线性的、顺序的。就像一条线,从起点(初始状态)开始,按照明确的、不可跳跃的顺序(阶段1 -> 阶段2 -> ... -> 阶段n)一步步推进到终点(最终状态)。整个决策过程形成一个清晰的“链条”。

2. 示意图解构

  • 状态:图中 状态0状态1, ..., 状态n,代表了在解决问题过程中,各个“时间点”或“步骤点”的情况记录。状态0初始状态状态n目标状态

  • 决策:图中 每一个阶段都有每一个阶段的决策,决策1决策2, ..., 决策n,代表从前一个状态向后一个状态转移时,我们所做出的选择。每个决策都会产生一定的“代价”或“收益”,并导致状态发生变化。

  • 阶段:图中底部的 阶段1阶段2, ..., 阶段n, 正是线性划分的体现。每个阶段对应一次“决策+状态转移”的过程。阶段与阶段之间首尾相接,顺序固定,构成了线性的求解流程。

  • 流程总结:整个算法从 状态0(初始条件)出发,在 阶段1根据 状态0做出 决策1,从而更新到 状态1;然后在 阶段2, 再基于 状态1做出 决策2, 更新到 状态2, 如此线性推进,直至在 阶段n做出 决策n后,到达最终的 状态n,问题得以解决。

3. 对“多维度状态”的说明

定义的第二句话是理解的难点和关键。它指出,即使状态包含多个维度(例如用坐标 (i, j)表示位置),只要每个维度的变化是按线性顺序进行遍历或递推的,就仍然属于线性DP。

  • 举例:在经典的“数字三角形”问题中,状态是二维的 (i, j),表示第i行第j列。我们通常的求解顺序是:从第一行开始,一行一行(线性遍历i)地向下计算,在每一行内,可能从左到右或按特定顺序(线性遍历j)计算每个位置的最优值。虽然状态是二维的,但两个维度(i和j)的变化过程本身都是线性、有序的阶段划分,因此它依然是线性DP。

总结

线性动态规划是一种将问题建模为“多阶段决策过程”的算法模型,其核心特征是阶段的线性、顺序依赖性。​ 无论状态是单一变量还是多维变量,只要决策推进的“时间线”或“逻辑顺序”是线性的,就可以用线性DP的思路来解决。图片中的示意图正是这一单向、链式决策过程的经典可视化表示。常见的最长上升子序列、背包问题、最短路径问题等,都是线性DP的典型应用。

实例讲解1: 爬楼梯

题目描述(HDU2041):一个楼梯共有M级台阶,刚开始时我们站在第1级台阶上,若每次只可以走上一级或二级台阶,则要走上第M级台阶共有多少种走法?

输入:第1行包含一个整数N,表示测试用例的个数。然后是N行数据,每行都包含一个整数M(1≤M≤40),表示楼梯的级数。

输出:对每个测试实例都输出不同走法的数量。

输入样例    输出样例
2           1
2           2
3

二、题意讲解

1. 问题理解

这是一个经典的动态规划问题,类似于斐波那契数列问题。我们需要计算从第1级台阶走到第M级台阶的不同走法数量,约束条件是:

  • 每次只能向上走1级2级台阶

  • 起始位置是第1级台阶

  • 目标位置是第M级台阶

2. 关键点分析

  • 当M=1时:已经在第1级,不需要走,有0种走法(不动)

  • 当M=2时:从第1级到第2级,只能走1步(1级),有1种走法

  • 当M=3时:从第1级到第3级,有两种方式:

    1. 先走1级到第2级,再走1级到第3级

    2. 直接走2级到第3级

      因此有2种走法

  • M=4:1+1+1、1+2、2+1,共3种。

三、解题思路

1. 数学递推关系

设f(n)表示从第1级走到第n级的走法数,则有以下递推关系:

  • f(1) = 0

  • f(2) = 1

  • f(3) = 2

  • f(n) = f(n-1) + f(n-2),当n ≥ 3时

解释:要到达第n级台阶,可以从第n-1级走1步上来,或者从第n-2级走2步上来。

只需要把n-1和n-2看成子问题,如果n-1对应a种走法,n-2对应b种走法,那么n级台阶就有a+b种走法。

1.原问题的最优解包含子问题的最优解,满足最优子结构

2.满足无后效性,因为从下往上走,和后边的子问题没有关系。

满足无后效性和最优子结构就可以使用动态规划。

2.解题方法

解法1:递归

/climbStairs_00_rec_00.cc


// 方法:递归
#include <iostream>
#include <vector>

using namespace std;

int fib(int n)
{
    if (n <= 3) {
        return n - 1;
    }
    return fib(n - 1) + fib(n - 2);
}

int main()
{
    int n = 0; // 测试数据组数
    int m = 0; // 楼梯级数(1≤M≤40)
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> m;
        cout << fib(m) << endl;
    }
    return 0;
}

复杂度分析

  • 时间复杂度:O(2ⁿ),因为递归树呈指数级增长。

  • 空间复杂度:O(n),递归调用栈深度。

优点

  • 代码极简,直观反映数学定义。

缺点

  • 效率极低,对于 M=40 就会产生大量重复计算,不可用。

解法2:记忆化递归(自顶向下DP)

/climbStairs_00_rec_01.cc

思路
在递归的基础上,使用一个数组 dp[] 保存已经计算过的 f(n) 值。当再次需要时直接返回,避免重复递归。


// 方法:记忆化递归
// 不需要重复求解已经计算过的数据
#include <iostream>
#include <vector>

using namespace std;

int recursion(int n, vector<int> & dp)
{
    if (n <= 3 || dp[n] != 0) {
        // != 0说明第n级楼梯的走法已经计算过,不需要重复计算,直接返回结果
        return dp[n];
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值