《算法竞赛从入门到国奖》算法基础:动态规划-路径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;
}
相关推荐
写代码写到手抽筋6 小时前
5G上行DCI字段判定:端口 流数 PMI选择详解
java·算法·5g
xieliyu.7 小时前
Java算法精讲:双指针(二)
java·开发语言·算法
苏宸啊7 小时前
IPC管道
linux·c++
BestOrNothing_20157 小时前
ROS2 话题通信实战:消息对象、Publisher 发布器与 Subscriber 订阅器保姆级教程
c++·ros2·subscriber·publisher·话题通信
wayz117 小时前
Momentum:PSL(心理线指标)技术指标详解
算法·金融·数据分析·量化交易·特征工程
8Qi88 小时前
LeetCode 213:打家劫舍 II(House Robber II)—— 题解 ✅
算法·leetcode·职场和发展·动态规划
三品吉他手会点灯8 小时前
C语言学习笔记 - 44.运算符和表达式 - 运算符2 - 除法与取余运算符
c语言·开发语言·笔记·算法
乐迪信息8 小时前
乐迪信息:AI算法盒子实时识别船舶烟雾与火焰异常
大数据·人工智能·算法·安全·目标跟踪
J-Tony118 小时前
【JVM】根可达算法
jvm·算法
艾iYYY9 小时前
string 类的模拟实现
android·服务器·c语言·c++·算法