week3 dp
一、实践报告
1.1 最优子结构与递归方程式
题意化简
题目要求从 N×N 网格的左上角 (1,1) 走到右下角 (N,N),每穿越一个方格花费 1 单位时间,必须在 (2N−1) 个单位时间内走出。从 (1,1) 到 (N,N) 的最短路径需要横向走 N−1 步、纵向走 N−1 步,共 2N−2 步,途经 2N−1 个格子,恰好等于时间上限。这意味着任何向上或向左的移动都会使步数超出限制,因此合法路径只能向右或向下移动
最优子结构
设商人当前位于格子 (i, j),要走到右下角 (N, N)。由于只能向右或向下,到达 (i, j) 的最后一步只能来自上方 (i−1, j) 或左方 (i, j−1)。
因此,"从 (1,1) 到 (i,j) 的最小费用路径"必然包含"从 (1,1) 到其前驱格子 (i−1,j) 或 (i,j−1) 的最小费用路径"。如果子路径不是最优的,将其替换为更优的子路径就能得到更优的总路径,与假设矛盾。故该问题具有最优子结构
递归方程式
定义状态:dp[i][j] 表示从左上角 (1,1) 走到格子 (i,j) 所需的最小累计费用(包含格子 (i,j) 本身的费用 a[i][j])。
转移方程:dp[i][j]=a[i][j]+min(dp[i−1][j],dp[i][j−1])
边界条件:起点: dp[1][1]=a[1][1];第一行只能从左边来;第一列只能从上面来
对于 i=0 或 j=0 的越界位置,设dp[0][j]=dp[i][0]=+∞ ,使其在min运算中自然被淘汰
1.2 填表法设计
表的维度: 二维数组 dp[105][105],行表示网格的行号 i,列表示网格的列号 j
填表范围: i∈[1,N] , j∈[1,N],共 N×N 个格子
填表顺序:按行优先(从上到下、从左到右)。因为计算时只依赖上方和左方 ,这两个值在填到 (i,j) 时一定已经被计算过
原问题最优值:对应表格元素 dp[N][N],即从左上角走到右下角的最小总费用
1.3 复杂度分析
时间复杂度: 共有 N² 个状态,每个状态的计算只需两次查表、一次取min 、一次加法,为 O(1)。总时间复杂度为 O(N²)。N < 100 时,运算量不超过 10⁴,低于 400ms 时间限制
空间复杂度: 使用了一个 (N+1)×(N+1) 的二维数组 dp,空间复杂度为 O(N²)
二、对动态规划算法的理解和体会
动态规划的核心思想可以概括为两个关键词:最优子结构和重叠子问题。
最优子结构:原问题的最优解包含了其子问题的最优解。换句话说,我们可以通过组合子问题的最优解来构造原问题的最优解。在"最低通行费"中,走到 (i,j) 的最优路径必然由走到其前驱格子的最优路径延伸而来,这就是最优子结构的体现。
重叠子问题:在递归求解过程中,同一个子问题会被反复计算多次。动态规划通过将子问题的解存入表中(即"记忆化"或"填表"),保证每个子问题只计算一次,从而避免重复计算。例如在填表过程中,每个格子只计算一次,后续再用到时直接查表。
与分治法的区别在于:分治法的子问题是互相独立的(如归并排序),而动态规划的子问题是相互重叠的。如果子问题不重叠,用分治即可;如果重叠,用动态规划才能显著提升效率。
设计动态规划算法的步骤通常为:
刻画最优解的结构特征(证明最优子结构)
递归地定义最优值(写出状态转移方程)
自底向上计算最优值(确定填表顺序,保证子问题先于依赖它的父问题被求解)
根据计算得到的信息构造最优解(如需要输出具体路径,可通过回溯实现)
通过本章的学习,我体会到动态规划最难的部分往往不是写出代码,而是如何正确地定义状态以及如何推导出转移方程。一个恰当的状态定义能让转移方程变得自然而简洁;反之,如果状态定义不当,转移方程就会异常复杂甚至无法成立。

浙公网安备 33010602011771号