算法第三次作业

实践报告

题目说明:(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])
$$

边界条件

  1. 起点:$\boldsymbol{(\boldsymbol{dp[1][1]=cost[1][1]})}$
  2. 第一行(只能从左边向右走,没有上方来源):
    $$
    dp[1][j]=dp[1][j-1]+cost[1][j],\quad j>1
    $$
  3. 第一列(只能从上方向下走,没有左侧来源):
    $$
    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;
}
posted @ 2026-11-03 23:01  戚群富  阅读(6)  评论(0)    收藏  举报