算法第三章作业
- 实践报告:动态规划法求解“7-4 最低通行费”
1.1 最优子结构与递归方程式
设网格为 a[i][j],表示第 i 行第 j 列小方格的费用,其中 1≤i,j≤N。
商人从左上角 (1,1) 进入,从右下角 (N,N) 出去。每穿越一个小方格花费 1 个单位时间,并且必须在 (2N-1) 个单位时间内穿越出去。由于从 (1,1) 到 (N,N) 至少需要经过 2N-1 个小方格,因此路径不能绕行,只能向右或向下移动。于是问题转化为:从 (1,1) 到 (N,N),每次只能向右或向下,求经过方格费用之和的最小值。
定义状态:
dp[i][j] 表示从 (1,1) 到 (i,j),且只向右、向下走的最小总费用。
由于到达 (i,j) 的最后一步只能来自上方 (i-1,j) 或左方 (i,j-1),因此递归方程式为:
dp[i][j] = a[i][j] + min(dp[i-1][j], dp[i][j-1]),其中 i>1,j>1。
边界条件为:
dp[1][1] = a[1][1]
第一行只能一直向右到达:
dp[1][j] = dp[1][j-1] + a[1][j],其中 j=2,3,...,N。
第一列只能一直向下到达:
dp[i][1] = dp[i-1][1] + a[i][1],其中 i=2,3,...,N。
也可以将越界位置视为无穷大,从而统一处理边界。
该问题具有最优子结构性质:若从 (1,1) 到 (i,j) 的最优路径最后一步来自上方,则其前缀路径必然是从 (1,1) 到 (i-1,j) 的最优路径;否则可用更优前缀替换,得到更优总路径。来自左方时同理。
1.2 填表法的表维度、填表范围、填表顺序及原问题最优值
填表法使用二维表 dp。
表的维度:二维表,大小为 N×N,下标范围为 1≤i,j≤N。
填表范围:所有 i=1,2,...,N,j=1,2,...,N。
填表顺序:按行从上到下、每行从左到右填写,即 i=1→N,j=1→N。也可以先初始化第一行和第一列,然后按行或按列填写。关键是计算 dp[i][j] 时,其上方的 dp[i-1][j] 和左方的 dp[i][j-1] 必须已经计算完成。
原问题的最优值:从左上角到右下角的最小费用为 dp[N][N]。
对题目样例,最终计算得到:
dp[5][5] = 109
与样例输出一致。
1.3 时间复杂度和空间复杂度
该算法共有 N^2 个状态,每个状态只需进行一次加法和一次比较,转移代价为 O(1)。
时间复杂度:O(N^2)。因为需要填写 N×N 个表格元素,每个元素常数时间完成。
空间复杂度:若使用二维数组保存状态,则空间复杂度为 O(N^2)。由于 dp[i][j] 只依赖上一行同列元素 dp[i-1][j] 和当前行左侧元素 dp[i][j-1],因此可以使用一维滚动数组优化,将额外空间降为 O(N)。若同时不保存整个输入矩阵、边读入边处理,则空间可进一步控制在一维数组规模。
本题 N<100,使用二维数组完全可行。
- 对动态规划算法的理解和体会
动态规划的核心是发现问题的“最优子结构”和“重叠子问题”。在本题中,从起点到某个格子的最优路径,一定由到达其上方或左方格子的最优路径扩展而来,因此可以把大问题拆成若干规模更小的子问题,并保存子问题结果避免重复计算。
本题的一个关键点是对时间条件的转化:题目要求必须在 (2N-1) 个单位时间内穿越出去,而从左上角到右下角至少需要经过 2N-1 个方格,因此路径不能绕行,只能向右或向下。这个条件把原本可能较复杂的四方向移动问题转化成了典型的网格路径动态规划问题。
在实现动态规划时,需要特别注意以下几点:
- 状态定义要清晰:状态必须能完整表示子问题,并且满足无后效性。本题中 dp[i][j] 表示到达 (i,j) 的最小费用,定义明确。
- 转移方程要基于最后一步分析:到达 (i,j) 只能从上方或左方来,因此转移方程自然成立。
- 边界条件不能遗漏:第一行和第一列只有一种走法,必须单独初始化。
- 填表顺序要保证依赖已计算:按行从上到下、从左到右填写,可以保证上方和左方状态已经求出。
- 注意空间优化:很多二维动态规划都可以通过滚动数组降维,本题即可将空间从 O(N^2) 优化到 O(N)。
通过本题可以体会到,动态规划不是简单的枚举,而是通过合理定义状态、建立转移关系、确定边界和计算顺序,把复杂问题分解为可复用的小问题。实际编程时还应结合样例验证结果,例如本题最终 dp[5][5]=109,与样例输出一致,说明状态和转移方程设计正确。

浙公网安备 33010602011771号