算法第三章上机实践报告

算法第三章《动态规划》上机实践报告

一.实践题目名称

最低通行费

 

二.问题描述

一个商人穿过一个N×N的正方形的网格,去参加一个非常重要的商务活动。他要从网格的左上角进,右下角出。每穿越中间1个小方格,都要花费1个单位时间。商人必须在(2N-1)个单位时间穿越出去。而在经过中间的每个小方格时,都需要缴纳一定的费用。

这个商人期望在规定时间内用最少费用穿越出去。请问至少需要多少费用?

注意:不能对角穿越各个小方格(即,只能向上下左右四个方向移动且不能离开网格)。

输入格式:

第一行是一个整数,表示正方形的宽度N (1≤N<100);

后面N行,每行N个不大于100的整数,为网格上每个小方格的费用。

输出格式:

至少需要的费用。

输入样例:

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

输出样例:

109

样例中,最小值为109=1+2+5+7+9+12+19+21+33。

 

三.算法描述

分析题目条件,商人从网格的左上角进,右上角出,且给定步数为2n-1,说明不能走回头路,只能向下或向右走。用二维数组dp[i][j]表示从左上角起点开始到ij列的最小花费,因为只能从上面或左边来,故每一格花费中的一部分是将到达其上面和到达其左边格子的花费(dp值)相比较,取二者较小值的格子作为到达本格的路线来源;另一部分是本格自身的花费,两部分费用相加即从起点到此的最小花费。使用循环语句自上而下填表,右下角终点格子的dp值即题目所求。

 

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std; int main() { int n, fee[101][101], dp[101][101]; cin >> n; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) cin >> fee[i][j]; dp[1][0] = 0; dp[0][1] = 0; //初始化起点左边和上方的格子 for (int j = 2; j <= n; j++) //初始化数组第一行 dp[0][j] = 200; for (int i = 2; i <= n; i++) //初始化数组第一列 dp[i][0] = 200; for (int i = 1; i <= n; i++) //从左到右、自上而下填表for (int j = 1; j <= n; j++) { dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + fee[i][j]; } cout << dp[n][n]; return 0; }

 

 

四.问题求解

1.根据最优子结构性质,列出递归方程式

dp[i][j]=min(dp[i-1][j]dp[i][j-1]) + fee[i][j]

 

2.给出填表法中表的维度、填表范围和填表顺序

表是n维的,为方便理解起见,舍弃第一行与第一列,从dp[1][1]开始,按自上而下、从左到右(即i1nj1n)的顺序填表,直到dp[n][n]

 

3.分析该算法的时间和空间复杂度

时间复杂度:采用二重循环填表,时间复杂度为O (n2)

空间复杂度:采用二维数组存放费用数据,空间复杂度为O (n2)

 

4.心得体会(对本次实践收获及疑惑进行总结)

本次实践难点在于分析行走的路线方向,找到“只能向右和向下走”这一解题突破口。其次是考虑边界条件并初始化舍弃的第一行第一列,先把起点上方和左边的格子置零,再把整个数组除所用数据以外全都设成比较大的数,从而忽略边界问题,使递推方程式在每一格都适用。

 

五.对动态规划算法的理解和体会

1.动态规划算法与分治法类似,基本思想是将待求解问题分解成若干子问题,先求解子问题,再结合这些子问题的解得到原问题的解。与之不同的是,动态规划算法通常用一个表来记录所有已解决的子问题的答案(无论该子问题以后是否被用到,只要它被计算过,就将其结果填入表中),以达到避免大量重复计算的目的。

2.动态规划法的运用步骤:

①找出最优解的性质,分析它位于表的哪个位置。

②递归定义最优值,写出递归方程式。

③确定填表的起点和方向。

④根据计算最优值得到的信息构造最优解。

posted @ 2021-10-26 19:11  布小林  阅读(52)  评论(0编辑  收藏  举报