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

💡Yupureki:个人主页

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

《个人在线OJ平台》


🌸Yupureki🌸的简介:


目录

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

算法原理

代码示例

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

算法原理

代码示例

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

算法原理

代码示例

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

算法原理

代码示例


1. 矩阵的最小路径和

题目链接:

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

算法原理

状态表示

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

状态方程

dpij = min(dpi - 1j, dpij - 1) + vij;

初始化

dp10 = 0;dp01=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 列是树,此时不考虑

dpij = (dpi+1j + dpij-1)%2333;

初始化

dpn+11 = 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. 状态表示 :dpij表示:到达**(i,j)**位置的方案数。那么fnm就是我们要的结果。
  2. 状态转移方程
    1. 如果(i,j)位置能走到,则到达(i,j)位置之前的一小步,有两种情况:
      1. 从(i-1,j)向下走一步,走到(i,j)此时的方案数为dpi-1j
      2. 从(i,j-1)向右走一步,走到(i,j)此时的方案数为dpij-1
      3. 那么总方案数dpij=dpi-1j+dpij-1
    2. 如果i,j位置走不到,dp\[ij=0。
  3. 初始化:我们可以给原始的矩阵多加一行多加一列,n,m,x,y全部+1,这样填任何一个位置都不会越界。然后初始化dp10=1或者dp01=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流,走到i1j1i2j2时,有四种情况:
    1. i1-1j1i2-1j2走到
    2. i1j1-1i2-1j2走到
    3. i1j1-1i2j2-1走到
    4. i1j1-1i2j2-1走到
    5. 那么dpsumi1i2 = max(dpsum-1i1i2,dpsum-1i1-1i2,dpsum-1i1-1i2-1,dpsum-1i1i2-1) + vi1sum - i1 + vi2sum - i2(如果i1 == i2,则只能加一次)
  3. 初始化:dp10 = 0;dp01 = 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;
}
相关推荐
用户805533698033 小时前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
To_OC13 小时前
LC 207 课程表:刚学图论那会儿,我连这是拓扑排序都没看出来
javascript·算法·leetcode
To_OC13 小时前
LC 208 实现 Trie 前缀树:曾被名字劝退,写完发现是送分题
javascript·算法·leetcode
BadBadBad__AK15 小时前
线段树维护区间 k 次方和
c++·数学·算法·stl
卷无止境1 天前
Eigen 库如何借助 OpenMP 加速计算
c++·后端
_清歌1 天前
DSpark 深度解读:DeepSeek-V4 如何用「半自回归」把推理速度提升 85%
算法
统计实现局1 天前
SVD 的三步走:双对角化、Givens 收敛、排序
算法
躬行见万象1 天前
《VLA 系列》UniLab 强化训练 | G1 机器人 |复现
算法
统计实现局1 天前
对称不定分解(Bunch-Kaufman):为什么 Cholesky 不够用
算法