算法第三章作业
实践报告
一、题目
一个商人穿过一个N×N的正方形的网格,去参加一个非常重要的商务活动。他要从网格的左上角进,右下角出。每穿越中间1个小方格,都要花费1个单位时间。商人必须在(2N-1)个单位时间穿越出去。而在经过中间的每个小方格时,都需要缴纳一定的费用。
这个商人期望在规定时间内用最少费用穿越出去。请问至少需要多少费用?
注意:不能对角穿越各个小方格(即,只能向上下左右四个方向移动且不能离开网格)。
输入格式:
第一行是一个整数,表示正方形的宽度N (1≤N<100);
后面N行,每行N个不大于100的整数,为网格上每个小方格的费用。
输出格式:
至少需要的费用。
二、分析
1.1 最优子结构、递归方程、边界条件
状态定义
设 dp[i][j] 表示:从起点 (1,1)走到方格(i,j)的最小总通行费用(包含格子(i,j)本身的费用)。
最优子结构说明
到达(i,j)的合法路径,只能来自两个位置:上方(i-1,j)或者左方(i,j-1)。 如果走到 (i,j)的最优路径是从(i-1,j)过来,那么从(1,1)to(i-1,j)这一段本身一定也是最优的;同理从左边过来时,(1,1)to(i,j-1)也是最优。满足动态规划最优子结构性质。
递归方程
dp[i][j] = a[i][j] + min(dp[i-1][j], dp[i][j-1])
边界条件
1.起点:dp[1][1] = a[1][1](起点只有自己的费用)
2.第一行(i=1,j>1):上面没有格子,只能从左边走来
dp[1][j] = dp[1][j-1] + a[1][j]3. 第一列(j=1,i>1):左边没有格子,只能从上面走来
dp[i][1] = dp[i-1][1] + a[i][1]
说明:下标这里采用从 1 开始,方便处理边界,避免判断越界。
1.2 填表法:表维度、填表范围、填表顺序、最优值位置
1.表的维度:二维数组dp[N+1][N+1](下标从 1 到 N,大小 (N+1)(N+1))
2.填表范围:
先填边界:第 1 行、第 1 列
再填主体:i 从 2~ N,j 从 2~N
3.填表顺序:从上到下,从左到右(逐行遍历;填 dp[i][j] 的时候,它需要的 dp[i-1][j](上一行,已填好)、dp[i][j-1](本行左边,已经填好)都已经计算完成)
4.原问题最优值:dp[N][N],就是从左上角走到右下角的最小通行费。
1.3 时间复杂度与空间复杂度
时间复杂度:O(N^2) 一共 NN 个状态,每个状态只做一次 min 加法,常数时间。N<100,计算量很小。
空间复杂度:O(N^2)需要二维 dp 数组保存所有子问题的最优解;可以直接在原费用数组上原地更新,不额外开 dp 数组,空间仍为 O(N^2);也可以滚动数组优化到 O(N),但本题 N 很小一般不要求。
三、对动态规划算法的理解和分析
1.动态规划不是简单的贪心:贪心是局部最优直接推全局最优;动态规划需要枚举所有可行子问题的最优结果,再做选择,依赖子问题的解。本题中每一格取上方、左边的最小值,就是在两个子问题最优解里选更优的。
2.两个必要条件:最优子结构 + 子问题重叠。 最优子结构保证:全局最优一定由子问题最优组合而来;子问题重叠保证我们值得用一张表保存结果,而不是反复递归重算。
3.设计 DP 的固定流程: (1)定义状态(最难一步,要清晰表达子问题含义)(2)推导状态转移方程(写出递归式) (3)确定边界、初始条件(4)确定填表顺序,保证依赖先被算出 (5)找到目标状态,输出答案
4.体会:写 DP 的时候状态定义一定要严谨,比如本题 dp [i][j] 是否包含当前格子费用,一旦定义错,整个转移方程和答案都会出错;填表顺序也很关键,如果顺序乱了,需要的子问题还没算出来,结果就是错的。

浙公网安备 33010602011771号