《算法竞赛从入门到国奖》算法基础:动态规划-路径dp

💡Yupureki:个人主页

✨个人专栏:《C++》 《算法》《Linux系统编程》《高并发内存池》《MySQL数据库》

《个人在线OJ平台》


🌸Yupureki🌸的简介:


目录

[1. 矩阵的最小路径和](#1. 矩阵的最小路径和)

算法原理

代码示例

[2. 「木」迷雾森林](#2. 「木」迷雾森林)

算法原理

代码示例

[3. 过河卒](#3. 过河卒)

算法原理

代码示例

[4. 方格取数](#4. 方格取数)

算法原理

代码示例


1. 矩阵的最小路径和

题目链接:

矩阵的最小路径和_牛客题霸_牛客网

算法原理

状态表示

dp[ i ][ j ]表示走到第 i 行,第 j 列的格子时,最小的路径和是多少

状态方程

dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + v[i][j];

初始化

dp[1][0] = 0;dp[0][1]=0

填表顺序

从上到下,从左到右

代码示例

cpp 复制代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> v(n + 1, vector<int>(m + 1, 0xffffff));
    vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0xffffff));
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            int num; cin >> num;
            v[i][j] = num;
        }
    }
    dp[1][0] = 0;
    dp[0][1] = 0;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
            dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + v[i][j];
    }
    cout << dp[n][m];
    return 0;
}

2. 「木」迷雾森林

题目链接:

「木」迷雾森林

算法原理

状态表示

dp[ i ][ j ]表示走到第 i 行 第 j 列的格子时,不同的方案是多少

状态方程

如果第 i 行 第 j 列是树,此时不考虑

dp[i][j] = (dp[i+1][j] + dp[i][j-1])%2333;

初始化

dp[n+1][1] = 1;

填表顺序

从下往上,从左到右

代码示例

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    int n, m;
    cin >> n >> m;
    vector<vector<int>> v(n + 1, vector<int>(m + 1, 1));
    vector<vector<int>> dp(n + 2, vector<int>(m + 2));
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            int num; scanf("%d",&num);
            v[i][j] = num;
        }
    }
    dp[n+1][1] = 1;
    for (int i = n; i >= 1; i--)
    {
        for (int j = 1; j <= m; j++)
        {
            if (v[i][j] == 1)
                continue;
            dp[i][j] = (dp[i+1][j] + dp[i][j-1])%2333;
        }
    }
    cout << dp[1][m];
    return 0;
}

3. 过河卒

题目链接:

P1002 [NOIP 2002 普及组] 过河卒 - 洛谷

算法原理

  1. 状态表示 :dp[i][j]表示:到达**(i,j)**位置的方案数。那么f[n][m]就是我们要的结果。
  2. 状态转移方程
    1. 如果(i,j)位置能走到,则到达(i,j)位置之前的一小步,有两种情况:
      1. 从(i-1,j)向下走一步,走到(i,j)此时的方案数为dp[i-1][j];
      2. 从(i,j-1)向右走一步,走到(i,j)此时的方案数为dp[i][j-1];
      3. 那么总方案数dp[i][j]=dp[i-1][j]+dp[i][j-1]。
    2. 如果[i,j位置走不到,dp[i][j]=0。
  3. 初始化:我们可以给原始的矩阵多加一行多加一列,n,m,x,y全部+1,这样填任何一个位置都不会越界。然后初始化dp[1][0]=1或者dp[0][1]=1,保证后续填表正确即可。
  4. 填表顺序:从上往下每一行,每一行从左往右。

代码示例

cpp 复制代码
#include <iostream>
#include <vector>
#include <set>
using namespace std;

int main()
{
    int n, m, x, y; cin >> n >> m >> x >> y;
    vector<vector<long long>> dp(n + 1, vector<long long>(m + 1));
    set<pair<int,int>> um;
    um.insert({ x,y });
    um.insert({ x + 1,y + 2 }); um.insert({ x + 2,y + 1 });
    um.insert({ x + 1,y - 2 }); um.insert({ x + 2,y - 1 });
    um.insert({ x - 1,y + 2 }); um.insert({ x - 2,y + 1 });
    um.insert({ x - 1,y - 2 }); um.insert({ x - 2,y - 1 });
    if(um.find({0,0}) == um.end())
        dp[0][0] = 1;
    for (int i = 0; i <= n; i++)
    {
        for (int j = 0; j <= m; j++)
        {
            long long n1 = 0;
            long long n2 = 0;
            if (i > 0)
                n1 = dp[i - 1][j];
            if (j > 0)
                n2 = dp[i][j - 1];
            if (i == 0 && j == 0 || um.find({i,j}) != um.end())
                continue;
            dp[i][j] = n1 + n2;
        }
    }
    cout << dp[n][m];
    return 0;
}

4. 方格取数

题目链接:

P1004 [NOIP 2000 提高组] 方格取数 - 洛谷

算法原理

  1. 状态转移 :我们不能用贪心算法或是使用两次dp,都能找出反例。最好的方法便是让两个dp同时进行 。既然是同时进行的,我们会发现在任意时刻两个dp执行流走过的步数是一定的,我们记为sum,如果知道了第一个dp的i1,第二个dp的i2,那么就能计算出j1和j2(j1 = sum - i1,j2 = sum - j2)。因此我们令dp[ sum ][ i1 ][ i2 ]:当步数为sum,第一条路为[ i1 ][ sum - i1],第二条路为[ i2 ][ sum - i2 ]时,此时的得到数的最大值
  2. 状态方程 :两个dp流,走到[i1][j1],[i2][j2]时,有四种情况:
    1. 从[i1-1][j1]和[i2-1][j2]走到
    2. 从[i1][j1-1]和[i2-1][j2]走到
    3. 从[i1][j1-1]和[i2][j2-1]走到
    4. 从[i1][j1-1]和[i2][j2-1]走到
    5. 那么dp[sum][i1][i2] = max(dp[sum-1][i1][i2],dp[sum-1][i1-1][i2],dp[sum-1][i1-1][i2-1],dp[sum-1][i1][i2-1]) + v[i1][sum - i1] + v[i2][sum - i2](如果i1 == i2,则只能加一次)
  3. 初始化:dp[1][0] = 0;dp[0][1] = 0
  4. 填表顺序:sum从1到2 * n的过程中,每次从上到下,从左到右填表

代码示例

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int dp[20][10][10];
int v[10][10];

int main()
{
    int n; cin >> n;
    int a, b, c;
    while (cin >> a >> b >> c, c)
    {
        v[a][b] = c;
    }
    for(int sum = 1;sum <= 2 * n;sum++)
    {
        for(int i1 = 1;i1<=n;i1++)
        {
            for(int i2 = 1;i2<=n;i2++)
            {
                int j1 = sum - i1;
                int j2 = sum - i2;
                if(j1 <= 0 || j2 <= 0 || j1 > n || j2 > n)
                    continue;
                int r1 = dp[sum-1][i1 - 1][i2-1];
                int r2 = dp[sum-1][i1][i2-1];
                int r3 = dp[sum-1][i1 - 1][i2];
                int r4 = dp[sum-1][i1][i2];
                if(i1 == i2)
                    dp[sum][i1][i2] = max(r1,max(r2,max(r3,r4))) + v[i1][j1];
                else
                    dp[sum][i1][i2] = max(r1,max(r2,max(r3,r4))) + v[i1][j1] + v[i2][j2];
            }
        }
    }
    cout << dp[2 * n][n][n];
    return 0;
}
相关推荐
三毛的二哥3 小时前
BEV:典型BEV算法总结
人工智能·算法·计算机视觉·3d
南宫萧幕4 小时前
自控PID+MATLAB仿真+混动P0/P1/P2/P3/P4构型
算法·机器学习·matlab·simulink·控制·pid
浪浪小洋5 小时前
c++ qt课设定制
开发语言·c++
charlie1145141915 小时前
嵌入式C++工程实践第16篇:第四次重构 —— LED模板,从通用GPIO到专用抽象
c语言·开发语言·c++·驱动开发·嵌入式硬件·重构
handler015 小时前
Linux: 基本指令知识点(2)
linux·服务器·c语言·c++·笔记·学习
故事和你915 小时前
洛谷-数据结构1-4-图的基本应用1
开发语言·数据结构·算法·深度优先·动态规划·图论
我叫黑大帅5 小时前
为什么map查找时间复杂度是O(1)?
后端·算法·面试
炽烈小老头5 小时前
【每天学习一点算法 2026/04/20】除自身以外数组的乘积
学习·算法
skilllite作者6 小时前
AI agent 的 Assistant Auto LLM Routing 规划的思考
网络·人工智能·算法·rust·openclaw·agentskills
破浪前行·吴7 小时前
数据结构概述
数据结构·学习