算法第三章作业2504骆俊智
1.1 最优子结构性质与递归方程式
(1)问题分析
商人从左上角 (1,1) 出发,到右下角 (N,N)。只允许上下左右移动,每经过一个方格花费 1 个单位时间。从 (1,1) 到 (N,N) 的最短路径需要 (N-1)+(N-1)=2N-2 步,也就是经过 2N-1 个方格。题目要求必须在 2N-1 个单位时间内穿越出去,因此不能走回头路,只能向右或向下。
所以问题转化为:从左上角走到右下角,每次只能向右或向下,求经过方格费用之和的最小值。
(2)状态定义
设 a[i][j] 表示第 i 行第 j 列方格的通行费。定义 f[i][j] 表示从起点 (1,1) 走到方格 (i,j) 的最小总费用,并且该费用包含方格 (i,j) 自身的费用。
(3)最优子结构性质
要到达方格 (i,j),最后一步只能来自上方方格 (i-1,j) 或左方方格 (i,j-1)。因此,到达 (i,j) 的最优路径,一定是由到达上方或左方的最优路径,再加上 (i,j) 的费用得到的。
如果到达 (i-1,j) 的某条路径不是最优路径,那么把它替换成更优路径后,到达 (i,j) 的总费用会更小,这与原路径最优矛盾。所以问题具有最优子结构性质。
(4)递归方程式
f[i][j] = a[i][j] + min{ f[i-1][j], f[i][j-1] } 2 ≤ i, j ≤ N
其中:
• f[i-1][j]:从上方到达 (i,j) 的最小费用;
• f[i][j-1]:从左方到达 (i,j) 的最小费用;
• a[i][j]:当前方格自身的费用。
(5)边界条件
起点:
f[1][1] = a[1][1]
第一行只能一直向右走:
f[1][j] = f[1][j-1] + a[1][j] 2 ≤ j ≤ N
第一列只能一直向下走:
f[i][1] = f[i-1][1] + a[i][1] 2 ≤ i ≤ N
1.2 填表法分析
(1)表的维度
使用二维表 f[1..N][1..N],逻辑维度是 N×N。代码实现时,为了方便使用从 1 开始的下标,可以开成 f[N+1][N+1],其中第 0 行和第 0 列不使用。
(2)填表范围
需要填写的范围是:
1 ≤ i ≤ N, 1 ≤ j ≤ N
其中,f[1][1] 直接赋值为 a[1][1];第一行 f[1][j] 从左往右递推;第一列 f[i][1] 从上往下递推;其余格子按照递归方程计算。
(3)填表顺序
采用从上到下、从左到右的顺序:
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
// 计算 f[i][j]
原因是 f[i][j] 只依赖于 f[i-1][j] 和 f[i][j-1]:前者在上一行,已经计算完成;后者在当前行的左边,也已经计算完成。
(4)原问题的最优值
原问题是从 (1,1) 走到 (N,N) 的最小总费用,因此原问题的最优值为:
f[N][N]
(5)样例验证
样例输入网格如下:
1 4 6 8 10
2 5 7 15 17
6 8 9 18 20
10 11 12 19 21
20 23 25 29 33
按照递归方程计算,得到动态规划表(表中第 i 行第 j 列即 f[i][j]):
i\j 1 2 3 4 5
1 1 5 11 19 29
2 3 8 15 30 46
3 9 16 24 42 62
4 19 27 36 55 76
5 39 50 61 84 109
因此:
f[5][5] = 109
最少费用为 109。
1.3 算法复杂度分析
(1)时间复杂度
状态总数为 N×N=N²。每个状态 f[i][j] 只需要做一次加法和一次比较,转移代价为 O(1)。
因此总时间复杂度为:
O(N²)
(2)空间复杂度
使用二维数组存储所有状态 f[N][N],所以空间复杂度为:
O(N²)
(3)空间优化
由于 f[i][j] 只依赖于上一行的 f[i-1][j] 和当前行左边的 f[i][j-1],因此可以把二维数组优化为一维滚动数组。优化后空间复杂度为:
O(N)
动态规划的关键是状态定义与转移。做“最低通行费”时,我先由 2N-1 的时间限制推出只能向右或向下,再令 f[i][j] 表示到达 (i,j) 的最小费用。状态清楚后,方程、边界和填表顺序自然清晰。动态规划不是背模板,而是把复杂问题拆成可复用的子问题。

浙公网安备 33010602011771号