算法第三章作业

实践报告:最低通行费
1.1 最优子结构、递归方程式、状态定义、边界条件
设dp[i][j]:从起点(0,0)(左上角)走到方格(i,j),并且只向右、向下走,到达(i,j)的最小总通行费用(包含方格(i,j)本身的费用)。
p[i][j]:方格 (i,j) 的通行费。

最优子结构说明
要到达(i,j),最后一步只可能来自上方(i-1,j)(向下走一步),或者左边(i,j-1)(向右走一步)。
如果走到(i,j)的路径是最优路径,那么走到前驱(i-1,j)或者(i,j-1)的路径一定也是对应子问题的最优路径。

最优子结构性质
递归方程 (dp[i][j] = min(dp[i-1][j], dp[i][j-1])+p[i][j],i>0,j>0

边界条件
1.起点:dp[0][0] = p[0][0],起点就是左上角格子,费用就是本格费用
2. 第一行(只能从左边向右走,没有上方格子):dp[0][j] = dp[0][j-1]+p[0][j],j>0
3. 第一列(只能从上方向下走,没有左边格子):dp[i][0] = dp[i-1][0]+p[i][0],i>0

1.2 填表法:表维度、填表范围、填表顺序、最优值位置
①表的维度:二维数组 dp[N][N],下标范围0<=i<=N-1, 0<=j<=N-1
②填表范围:i:0 ~ N-1;j:0 ~ N-1
③填表顺序:行优先(从上到下,每行内部从左往右)
理由:计算dp[i][j]需要上方dp[i-1][j](上一行已经算完)、左侧dp[i][j-1](本行左边已经算完)。
也可以列优先(从左到右,每列从上往下)。
④原问题最优值:dp[N-1][N-1]

1.3 复杂度
时间复杂度:O(N^2),一共 N^2 个状态
空间复杂度:O(N^2),N*N 的 dp 数组

核心代码实现:
const int MAX = 105;
int dp[MAX][MAX]; //走到坐标(i,j)这个格子时,花费的最小总费用。

void mix_prince(int n, int p[][MAX]){
int i, j;
dp[0][0] = p[0][0];
for(i = 1; i < n; i++){
dp[i][0] = dp[i-1][0] + p[i][0];
}
for(j = 1; j < n; j++){
dp[0][j] = dp[0][j-1] + p[0][j];
}
for(i = 1; i < n; i++){
for(j = 1; j < n; j++){
dp[i][j] = min(dp[i][j-1], dp[i-1][j]) + p[i][j];
}
}
}

int main(){
int price[MAX][MAX], i, j, N;
cin >> N;
for(i = 0; i < N; i++){
for(j = 0; j < N; j++){
cin >> price[i][j];
}
}
mix_prince(N, price);
cout << dp[N-1][N-1] << endl;
return 0;
}

2 对动态规划算法的理解和体会
动态规划算法的核心思想是将规模较大的原问题拆解为一系列规模更小、结构相同的子问题,存储子问题的计算结果,避免对子问题的重复求解,实现以空间换时间。
核心性质:最优子结构和重叠子问题。
最优子结构是指原问题的最优解可以由相关子问题的最优解构造而来,这是我们推导状态转移方程的基础;
重叠子问题是指求解不同子问题时会反复用到同一个更小的子问题,这也是动态规划能够提升效率的关键。

设计动态规划问题时,首要任务是准确定义状态,状态定义直接决定后续转移方程的形式;接着梳理状态之间的依赖关系,写出转移方程,同时单独处理边界条件;最后根据依赖关系确定填表顺序,按照从小到大的顺序依次求解子问题,逐步推导出原问题的最优解。
动态规划本质上是递推思想,从简单的基础子问题出发,不断利用已求出的子问题结果,最终得到原问题答案。

posted @ 2026-11-01 21:32  pratle  阅读(5)  评论(0)    收藏  举报