《算法竞赛从入门到国奖》算法基础:动态规划-路径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;
}
相关推荐
副露のmagic2 小时前
数组章节 leetcode 思路&实现
算法·leetcode·职场和发展
荣光属于凯撒2 小时前
P2176 [USACO11DEC] RoadBlock S / [USACO14FEB] Roadblock G/S
算法·图论
雨季mo浅忆3 小时前
记录利用Cursor快速实现拖拽式问卷题型创建
算法
321.。3 小时前
Linux 进程控制深度解析:从创建到替换的完整指南
linux·开发语言·c++·学习
Yzzz-F3 小时前
2018-2019 ACM-ICPC, Asia Dhaka Regional ContestC[数论]
算法
小Tomkk3 小时前
怎么配置 Visual Studio Code 配置 C/C++
c语言·c++·vscode
Frostnova丶3 小时前
LeetCode 3474. 字典序最小的生成字符串
算法·leetcode·职场和发展
REDcker3 小时前
Nagle 算法与 TCP_NODELAY、TCP_CORK 详解
网络·tcp/ip·算法
CheerWWW3 小时前
C++学习笔记——枚举、继承、虚函数、可见性
c++·笔记·学习