算法第三次作业
实践报告
题目说明:(N\times N)网格,起点((1,1)),终点(N,N),必须在 $2N-1$ 时间到达。
$2N-1$ 步的含义:从左上到右下,一共要走 $2N-2$ 步,经过 $2N-1$ 个格子。
只能向右、向下走(如果允许上下左走,就会步数超标,超过 $2N-1$,不符合时间限制),所以等价于:仅右、下移动,求路径总费用最小。
约定:$(cost[i][j])$ 表示第i行第j列方格的通行费;
$(dp[i][j])$:从起点 $((1,1))$ 走到 $((i,j))$ 的最小总费用。
1.1 根据最优子结构性质,列出递归方程式,说明方程式的定义、边界条件
状态定义
$(dp[i][j])$:从左上角 $((1,1))$ 到达方格 $((i,j))$ 的最小通行总费用。
走到 $((i,j))$,只能从上方 $(i-1,j)$ 向下走,或者从左侧 $(i,j-1)$ 向右走。到达 $((i,j))$ 的最优路径一定是这两条路径里代价更小的一条,加上当前格子费用,满足最优子结构。
递归方程
$$
(dp[i][j])=\min\big(dp[i-1][j],\ dp[i][j-1]\big)+(cost[i][j])
$$
边界条件
- 起点:$\boldsymbol{(\boldsymbol{dp[1][1]=cost[1][1]})}$
- 第一行(只能从左边向右走,没有上方来源):
$$
dp[1][j]=dp[1][j-1]+cost[1][j],\quad j>1
$$ - 第一列(只能从上方向下走,没有左侧来源):
$$
dp[i][1]=dp[i-1][1]+cost[i][1],\quad i>1
$$
为什么不能上下左右随便走?
如果回头走(上、左),经过格子数量就会大于 $2N-1$,违反时间限制,因此合法路径只有向右、向下。
1.2 填表法中表的维度、填表范围和填表顺序。以及原问题的最优值是哪个表格元素
- 表维度:二维dp表,N行N列
- 填表范围:$i=1,2,\dots,N$;$j=1,2,\dots,N$
- 填表顺序:从上到下,每行内部从左到右(行优先)
理由:计算 $(dp[i][j])$ 需要 $dp[i-1][j]$(上一行,已经算完)和 $dp[i][j-1]$(本行左边,已经算完),保证依赖的子问题先求解。 - 原问题最优值:$\boldsymbol{(\boldsymbol{dp[N][N]})}$
1.3 时间、空间复杂度
- 时间复杂度:$\boldsymbol{O(N^2)}$
一共 $N^2$ 个状态,每个状态一次min和加法,$(O(1))$ 运算。 - 空间复杂度:$\boldsymbol{O(N^2)}$
标准二维填表版本,存储 $N\times N$ 的dp数组;也可以优化为一维数组,空间降到 $(O(N))$。
2. 你对动态规划算法的理解和体会
动态规划的核心是利用最优子结构和重叠子问题,把大问题拆成同类型的小问题,保存子问题的解避免重复计算。
本题的时间限制 $2N-1$ 是关键约束,它直接限定了移动方向,排除了绕路的情况,让问题可以用动态规划求解。如果没有这个时间限制,允许随便上下左右走,就变成最短路问题(Dijkstra),不能直接用这个DP方程。
填表法是自底向上DP很直观的实现方式,先初始化边界,再按依赖顺序逐个填表。写DP的难点在于准确定义状态,状态定义清楚之后,递推式、边界条件自然就能推导出来。
对比贪心:贪心每次选局部最优,不一定得到全局最优;动态规划会枚举所有可行子方案,保留最优值,适合这类求最小代价路径的问题。动态规划需要仔细分析题目隐藏约束,很多题的限制条件决定了状态转移是否成立。
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int N;
cin >> N;
int cost[105][105];
int dp[105][105];
for(int i=1;i<=N;i++)
{
for(int j=1;j<=N;j++)
{
cin >> \(cost[i][j]\);
}
}
\(\boldsymbol{dp[1][1]=cost[1][1]}\);
for(int j=2;j<=N;j++)
{
dp[1][j]=dp[1][j-1]+cost[1][j];
}
for(int i=2;i<=N;i++)
{
dp[i][1]=dp[i-1][1]+cost[i][1];
}
for(int i=2;i<=N;i++)
{
for(int j=2;j<=N;j++)
{
\(dp[i][j]\)=min(dp[i-1][j],dp[i][j-1])+\(cost[i][j]\);
}
}
cout << \(\boldsymbol{dp[N][N]}\) << endl;
return 0;
}

浙公网安备 33010602011771号