第三章算法作业
一、题目关键约束分析
这道题最巧妙的伏笔藏在时间限制里。从 (1,1) 走到 (N,N),最短路径需要经过 2N-1 个格子(横向走 N-1 步,纵向走 N-1 步,加上起点共 2N-1 格)。时间上限恰好等于最短路径耗时,这意味着商人一步都不能绕---只能向右或向下走。一旦向左或向上,总步数必然超过 2N-1,违反时间限制。
于是问题转化为经典模型:在只能向右、向下移动的约束下,求从左上角到右下角路径费用之和的最小值。
二、动态规划求解步骤
3.1 最优子结构与递归方程式
设 dp[i][j] 为从起点 (1,1) 走到格子 (i,j) 所需缴纳的最少总费用,a[i][j] 为格子 (i,j) 的通行费。
由于只能向右或向下走,到达 (i,j) 的上一步只可能来自上方 (i-1,j) 或左方 (i,j-1)。若一条经过 (i,j)的路径是最优的,其起点到 (i,j)的子路径必然也是到达 (i,j) 的最优路径,否则替换后可得更优解,矛盾——问题具有最优子结构。
状态转移方程:
dp[i][j] = a[i][j] + min(dp[i-1][j],dp[i][j-1])
边界条件:
- 起点:dp[1][1] = a[1][1]
- 第一行:dp[1][j] = dp[1][j-1] + a[1][j](只能从左来)
- 第一列:dp[i][1] = dp[i-1][1] + a[i][1](只能从上来)
3.2 填表法设计
表的维度:二维表 𝑑 𝑝 [ 𝑁 ] [ 𝑁 ] (或 ( 𝑁 + 1 ) × ( 𝑁 + 1 ) ),与网格一一对应。
填表范围: 1 ≤ 𝑖 ≤ 𝑁 , 1 ≤ 𝑗 ≤ 𝑁 ,即整个 𝑁 × 𝑁 的区域。
填表顺序:由递归式可知, 𝑑 𝑝 [ 𝑖 ] [ 𝑗 ] 依赖于它上方和左方的元素,因此必须保证计算 ( 𝑖 , 𝑗 ) 时 ( 𝑖 − 1 , 𝑗 ) 和 ( 𝑖 , 𝑗 − 1 ) 已填好。采用按行优先、从左到右、从上到下的顺序填表即可满足依赖关系(按列优先同样可行)。
原问题的最优值:表格右下角的元素 𝑑 𝑝 [ 𝑁 ] [ 𝑁 ] ,即到达出口格子时的最少累计费用。
3.3 复杂度分析
时间复杂度 O(N^2):共 N^2 个格子,每个格子只做一次比较与加法。
空间复杂度 O(N^2):使用二维表。由于计算第 i 行只依赖第 i-1行和本行左侧,可用滚动数组压缩到 O(N)。
四、我对动态规划的理解和体会
第一,识别约束是建模的前提。"2N-1 时间限制"看似只是输入条件,实则隐含了"只能向右、向下"的关键约束,消除了后效性,使问题满足动态规划的适用条件。做 DP 题先别急着写转移方程,先想清楚问题隐含的结构。
第二,最优子结构是动态规划的灵魂。状态的定义必须做到"无论之前怎么走,到达这个格子后的最优决策只取决于这个状态本身"。状态定义得干净,转移方程往往水到渠成。
第三,边界条件决定成败。很多 DP 错误不是出在转移方程,而是出在第一行、第一列这类边界的初始化上。写代码前先在纸上填一个 3 * 3 的小表格走一遍流程,是最有效的自检方式。
第四,空间优化体现对状态依赖的深刻理解。只有真正弄清楚"每个状态到底依赖谁",才敢放心地把二维表压成一维滚动数组。
总的来说,动态规划不只是一种算法技巧,更是一种"以空间换时间、以记录换重复"的思维方式:承认子问题会被反复需要,就诚实地把它存下来。这种思想在操作系统缓存、数据库索引乃至日常的备忘习惯中都能看到影子。

浙公网安备 33010602011771号