侧边栏壁纸
博主头像
牛马 OJ博主等级

行动起来,活在当下

  • 累计撰写 6 篇文章
  • 累计创建 2 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

二维图DP

Administrator
2024-09-22 / 0 评论 / 0 点赞 / 94 阅读 / 25754 字

一、考察频率以及难度

考察频率:⭐⭐⭐⭐⭐

笔试题难度:⭐⭐⭐

算法难度:⭐⭐⭐

一般是笔试的中等或者困难题。

二、学习技巧

掌握好常见的DP状态转移以及初始值的填写

三、应用场景

这类题的标志非常明显,一般就是:给定一个二维的矩阵,每次只能往右或者往下走,求走到终点的最大值、最小值、方案数等。

注意,如果是可以往四个方向走,那就可能不能dp了,因为可能会导致dp的状态循环依赖。

四、算法讲解

在网格DP中,我们通常通过动态规划表(一个矩阵)来记录每个位置的状态信息,并通过递推关系来更新这些状态。常见的递推关系基于当前位置与其相邻位置的关系。

举个例子:

假设我们有一个网格图,每个格子有一个权重,我们需要找到从左上角(0,0)到右下角(n-1,m-1)的最小路径和。可以用网格DP的方法来解决这个问题。

  1. 定义状态:

  • dp[i][j]表示从左上角到位置(i,j)的最小路径和。

  1. 初始状态:

  • dp[0][0] = grid[0][0],因为它是起点。

  1. 状态转移方程:

  • 对于第一行(即i=0),只能从左边过来:dp[0][j] = dp[0][j-1] + grid[0][j]

  • 对于第一列(即j=0),只能从上面过来:dp[i][0] = dp[i-1][0] + grid[i][0]

  • 对于其他位置:dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]

  1. 最终结果:

  • dp[n-1][m-1]就是我们要找的最小路径和。

五、例题与解析

习题1 小红走迷宫

https://oj.niumacode.com/problem/P1111

小红位于一个 m x n 网格的左上角 。

小红每次只能向下或者向右移动一步。机器人试图达到网格的右下角。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。

示例 1:

输入

3 3
0 0 0
0 1 0
0 0 0

输出

2

示例 2:

输入

2 2
0 1
0 0

输出

1

提示:

  • m == obstacleGrid.length

  • n == obstacleGrid[i].length

  • 1 <= m, n <= 100

  • obstacleGrid[i][j] 为 0 或 1

题目解析

状态定义:f[i][j] 走到坐标(i,j)的方案数

状态转移:

  1. 假设该位置存在障碍物,那么f[i][j]就是0.

  2. 否则应该是可能由两个状态转移而来:f[i-1][j]+f[i][j-1]。

初始值:

  1. 对于第一行(即i=0),只能从左边过来;

  2. 对于第一列(即j=0),只能从上面过来

CPP

#include <iostream>
#include <vector>
using namespace std;

int main() {
    int m, n;
    cin >> m >> n;
    vector<vector<int>> obstacleGrid(m, vector<int>(n));
    
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            cin >> obstacleGrid[i][j];
        }
    }
    
    vector<vector<int>> dp(m, vector<int>(n, 0));
    dp[0][0] = obstacleGrid[0][0] == 1 ? 0 : 1;
    
    for (int i = 1; i < n; i++) {
        dp[0][i] = obstacleGrid[0][i] == 1 ? 0 : dp[0][i - 1];
    }
    
    for (int i = 1; i < m; i++) {
        dp[i][0] = obstacleGrid[i][0] == 1 ? 0 : dp[i - 1][0];
    }
    
    for (int i = 1; i < m; i++) {
        for (int j = 1; j < n; j++) {
            dp[i][j] = obstacleGrid[i][j] == 1 ? 0 : dp[i - 1][j] + dp[i][j - 1];
        }
    }
    
    cout << dp[m - 1][n - 1] << endl;
    
    return 0;
}

Java

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        int m = scanner.nextInt();
        int n = scanner.nextInt();
        int[][] obstacleGrid = new int[m][n];
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                obstacleGrid[i][j] = scanner.nextInt();
            }
        }
        
        int[][] dp = new int[m][n];
        dp[0][0] = obstacleGrid[0][0] == 1 ? 0 : 1;
        
        for (int i = 1; i < n; i++) {
            dp[0][i] = obstacleGrid[0][i] == 1 ? 0 : dp[0][i - 1];
        }
        
        for (int i = 1; i < m; i++) {
            dp[i][0] = obstacleGrid[i][0] == 1 ? 0 : dp[i - 1][0];
        }
        
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = obstacleGrid[i][j] == 1 ? 0 : dp[i - 1][j] + dp[i][j - 1];
            }
        }
        
        System.out.println(dp[m - 1][n - 1]);
        
        scanner.close();
    }
}

Python

m,n = int(input().split())
obstacleGrid = []
for _ in range(n):
    obstacleGrid.append([int(c) for c in input().split()])

dp = [[0] * n for _ in range(m)]
dp[0][0] = 0 if obstacleGrid[0][0] == 1 else 1
for i in range(1, n):
    dp[0][i] = 0 if obstacleGrid[0][i] == 1 else dp[0][i - 1]
for i in range(1, m):
    dp[i][0] = 0 if obstacleGrid[i][0] == 1 else dp[i - 1][0]
for i in range(1, m):
    for j in range(1, n):
        dp[i][j] = 0 if obstacleGrid[i][j] == 1 else dp[i - 1][j] + dp[i][j - 1]

print(dp[m - 1][n - 1])

习题2 小红走迷宫Ⅱ

https://oj.niumacode.com/problem/P1112

小红正在探索一个神奇的迷宫,这个迷宫是由一个 n*n 的整数矩阵构成的。在这个迷宫中,每个格子都有一个数字,代表它的能量值。小红的任务是找到一条从上到下的路径,使得路径上所有格子的数字和最小,但她必须遵循以下规则:她每一步只能向下走到下一行,并且不能走到同一列的格子。

示例1

输入

3
1 2 3
4 5 6
7 8 9

输出

13

示例 2:

输入

1
7

输出

7

提示:

  • n grid.length grid[i].length

  • 1 <= n <= 200

  • -99 <= grid[i][j] <= 99

题目解析

最朴素的解法其实很简单:

  • f[i,j]:到达位置[i,j]的最小路径和是多少

  • 转移: MIN(f[i+1,k] + grid[i][j]),其中k不等于j。

由于需要枚举k,所以复杂度是O(n^3),题目n是200,因此可以AC。

Python

from math import inf
n = int(input())
grid = []
for _ in range(n):
    grid.append([int(c) for c in input().split()])

f =[[inf]*n for _ in range(n)]
for j in range(n):f[-1][j] = grid[-1][j]
for i in range(n-2,-1,-1):
    for j in range(n):
        for nj in range(n):
            if nj != j:
                f[i][j] = min(f[i][j], f[i+1][nj] + grid[i][j])
print( min([f[0][j] for j in range(n)]))

C++

#include <iostream>
#include <vector>
#include <algorithm>
#include <limits.h>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<vector<int>> grid(n, vector<int>(n));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> grid[i][j];
        }
    }
    
    vector<vector<int>> f(n, vector<int>(n, INT_MAX));
    
    for (int j = 0; j < n; j++) {
        f[n - 1][j] = grid[n - 1][j];
    }
    
    for (int i = n - 2; i >= 0; i--) {
        for (int j = 0; j < n; j++) {
            for (int nj = 0; nj < n; nj++) {
                if (nj != j) {
                    f[i][j] = min(f[i][j], f[i + 1][nj] + grid[i][j]);
                }
            }
        }
    }
    
    int result = INT_MAX;
    for (int j = 0; j < n; j++) {
        result = min(result, f[0][j]);
    }
    
    cout << result << endl;
    
    return 0;
}

Java

import java.util.Scanner;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        int n = scanner.nextInt();
        int[][] grid = new int[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                grid[i][j] = scanner.nextInt();
            }
        }
        
        int[][] f = new int[n][n];
        for (int[] row : f) {
            Arrays.fill(row, Integer.MAX_VALUE);
        }
        
        for (int j = 0; j < n; j++) {
            f[n - 1][j] = grid[n - 1][j];
        }
        
        for (int i = n - 2; i >= 0; i--) {
            for (int j = 0; j < n; j++) {
                for (int nj = 0; nj < n; nj++) {
                    if (nj != j) {
                        f[i][j] = Math.min(f[i][j], f[i + 1][nj] + grid[i][j]);
                    }
                }
            }
        }
        
        int result = Integer.MAX_VALUE;
        for (int j = 0; j < n; j++) {
            result = Math.min(result, f[0][j]);
        }
        
        System.out.println(result);
        
        scanner.close();
    }
}

优化

不难发现,其实第三层循环目的是找到一个与当前纵坐标不等的最小值。因此,我们只需要维护f[i+1]的最小值和次小值即可,因为如果最小值是与当前纵坐标相等,那么就取次小值;不断更新对于每一个f[i]的最小值和次小值即可。

Python

class Solution:
    def minFallingPathSum(self, grid: List[List[int]]) -> int:
        n = len(grid)
        f =[[inf]*n for _ in range(n)]
        MIN1, MIN2 = [inf -1], [inf, -1]  # 最大 次大
        for j in range(n):
            cur = f[-1][j] = grid[-1][j]
            if MIN1[0] > cur:
                if MIN1[0] < MIN2[0]:
                    MIN2 = MIN1
                MIN1 = [cur, j]
            elif MIN2[0] > cur:
                MIN2 = [cur, j]

        for i in range(n-2,-1,-1):
            for j in range(n):

                if j != MIN1[1]:
                    f[i][j] = min(f[i][j], MIN1[0] + grid[i][j])
                else:
                    f[i][j] = min(f[i][j], MIN2[0] + grid[i][j])

            MIN1,MIN2 = [inf,-1],[inf,-1]

            for j in range(n):
                if MIN1[0] > f[i][j]:
                    if MIN1[0] < MIN2[0]:
                        MIN2 = MIN1
                    MIN1 = [f[i][j], j]
                elif MIN2[0] > f[i][j]:
                    MIN2 = [f[i][j], j]
        return min([f[0][j] for j in range(n)])

Java

import java.util.Scanner;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        int n = scanner.nextInt();
        int[][] grid = new int[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                grid[i][j] = scanner.nextInt();
            }
        }
        
        int[][] f = new int[n][n];
        for (int[] row : f) {
            Arrays.fill(row, Integer.MAX_VALUE);
        }
        
        int[] MIN1 = {Integer.MAX_VALUE, -1}; // 最小值和其索引
        int[] MIN2 = {Integer.MAX_VALUE, -1}; // 次小值和其索引
        
        for (int j = 0; j < n; j++) {
            int cur = f[n - 1][j] = grid[n - 1][j];
            if (MIN1[0] > cur) {
                if (MIN1[0] < MIN2[0]) {
                    MIN2 = MIN1.clone();
                }
                MIN1 = new int[]{cur, j};
            } else if (MIN2[0] > cur) {
                MIN2 = new int[]{cur, j};
            }
        }
        
        for (int i = n - 2; i >= 0; i--) {
            for (int j = 0; j < n; j++) {
                if (j != MIN1[1]) {
                    f[i][j] = Math.min(f[i][j], MIN1[0] + grid[i][j]);
                } else {
                    f[i][j] = Math.min(f[i][j], MIN2[0] + grid[i][j]);
                }
            }
            
            MIN1 = new int[]{Integer.MAX_VALUE, -1};
            MIN2 = new int[]{Integer.MAX_VALUE, -1};
            
            for (int j = 0; j < n; j++) {
                if (MIN1[0] > f[i][j]) {
                    if (MIN1[0] < MIN2[0]) {
                        MIN2 = MIN1.clone();
                    }
                    MIN1 = new int[]{f[i][j], j};
                } else if (MIN2[0] > f[i][j]) {
                    MIN2 = new int[]{f[i][j], j};
                }
            }
        }
        
        int result = Integer.MAX_VALUE;
        for (int j = 0; j < n; j++) {
            result = Math.min(result, f[0][j]);
        }
        
        System.out.println(result);
        
        scanner.close();
    }
}

C++

#include <iostream>
#include <vector>
#include <limits.h>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<vector<int>> grid(n, vector<int>(n));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> grid[i][j];
        }
    }
    
    vector<vector<int>> f(n, vector<int>(n, INT_MAX));
    vector<int> MIN1 = {INT_MAX, -1}; // 最小值和其索引
    vector<int> MIN2 = {INT_MAX, -1}; // 次小值和其索引
    
    for (int j = 0; j < n; j++) {
        int cur = f[n - 1][j] = grid[n - 1][j];
        if (MIN1[0] > cur) {
            if (MIN1[0] < MIN2[0]) {
                MIN2 = MIN1;
            }
            MIN1 = {cur, j};
        } else if (MIN2[0] > cur) {
            MIN2 = {cur, j};
        }
    }
    
    for (int i = n - 2; i >= 0; i--) {
        for (int j = 0; j < n; j++) {
            if (j != MIN1[1]) {
                f[i][j] = min(f[i][j], MIN1[0] + grid[i][j]);
            } else {
                f[i][j] = min(f[i][j], MIN2[0] + grid[i][j]);
            }
        }
        
        MIN1 = {INT_MAX, -1};
        MIN2 = {INT_MAX, -1};
        
        for (int j = 0; j < n; j++) {
            if (MIN1[0] > f[i][j]) {
                if (MIN1[0] < MIN2[0]) {
                    MIN2 = MIN1;
                }
                MIN1 = {f[i][j], j};
            } else if (MIN2[0] > f[i][j]) {
                MIN2 = {f[i][j], j};
            }
        }
    }
    
    int result = INT_MAX;
    for (int j = 0; j < n; j++) {
        result = min(result, f[0][j]);
    }
    
    cout << result << endl;
    
    return 0;
}

习题3 神奇世界游戏

https://oj.niumacode.com/problem/P1113

小红最近迷上了一款新游戏,这款游戏的场景是一个由 的整数矩阵构成的神奇世界。在这个世界中,每个格子代表一个地点,格子中的数字代表该地点的高度,但是小红在移动的过程只能从低爬到高,任务是找到一条从任何一个地点出发的 最长递增路径。在这条路径上,每一步她只能向上、下、左、右四个方向移动,不能在对角线方向上移动或移动到边界外。

示例 1:

输入:

3 3
9 9 4
6 6 8
2 1 1

输出:

4

解释:最长递增路径为 [1, 2, 6, 9]。

示例 2:

输入:

3 3
3 4 5
3 2 6
2 2 1

输出:

解释:最长递增路径是 [3, 4, 5, 6]。注意不允许在对角线方向上移动。

示例 3:

输入:

1 1
1

输出:

1

 

提示:

m == matrix.length

n == matrix[i].length

1 <= m, n <= 200

0 <= matrix[i][j] <= 2^31 - 1

题目解析

这个题使用记忆化搜索来首先更为方便。

状态定义:f(i,j)表示从点i,j出发的最长递增路径的长度。

状态转移:取四个方向的最大值,max(f(i+1,j), f(i-1,j), f(i,j+1), f(i,j-1)) + 1。

PS:这个依赖关系咋一看是成循环依赖的,为什么还可以进行动态规划呢?是因为题目需要满足严格递增,所以实际上是不会成环的。

CPP

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

vector<vector<int>> matrix;
vector<vector<int>> mem;
int m, n;
vector<pair<int, int>> dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

int dfs(int x, int y) {
    if (mem[x][y] != 0) {
        return mem[x][y];
    }

    int ans = 1;
    for (auto dir : dirs) {
        int nx = x + dir.first;
        int ny = y + dir.second;
        if (nx >= 0 && nx < m && ny >= 0 && ny < n && matrix[nx][ny] > matrix[x][y]) {
            ans = max(ans, dfs(nx, ny) + 1);
        }
    }
    mem[x][y] = ans;
    return ans;
}

int solve() {
    if (matrix.empty() || matrix[0].empty()) {
        return 0;
    }

    mem.assign(m, vector<int>(n, 0));
    int ans = 1;
    
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            ans = max(ans, dfs(i, j));
        }
    }
    
    return ans;
}

int main() {
    cin >> m >> n;
    matrix.resize(m, vector<int>(n));
    
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            cin >> matrix[i][j];
        }
    }
    
    cout << solve() << endl;
    
    return 0;
}

Java

import java.util.Scanner;

public class Main {
    private static int[][] matrix;
    private static int[][] mem;
    private static int m, n;
    private static int[][] dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        m = scanner.nextInt();
        n = scanner.nextInt();
        matrix = new int[m][n];
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                matrix[i][j] = scanner.nextInt();
            }
        }
        
        System.out.println(solve());
        
        scanner.close();
    }

    private static int solve() {
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return 0;
        }

        mem = new int[m][n];
        int ans = 1;
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                ans = Math.max(ans, dfs(i, j));
            }
        }
        
        return ans;
    }

    private static int dfs(int x, int y) {
        if (mem[x][y] != 0) {
            return mem[x][y];
        }

        int ans = 1;
        for (int[] dir : dirs) {
            int nx = x + dir[0];
            int ny = y + dir[1];
            if (nx >= 0 && nx < m && ny >= 0 && ny < n && matrix[nx][ny] > matrix[x][y]) {
                ans = Math.max(ans, dfs(nx, ny) + 1);
            }
        }
        mem[x][y] = ans;
        return ans;
    }
}

Python

class Solution:
    def longestIncreasingPath(self, matrix: List[List[int]]) -> int:
        if not matrix or not matrix[0]:
            return 0
        
        m, n = len(matrix), len(matrix[0])
        mem = [[0] * n for _ in range(m)]
        dirs = [(0, 1), (1, 0), (0, -1), (-1, 0)]
        
        def dfs(x, y):
            if mem[x][y] != 0:
                return mem[x][y]
            ans = 1
            for dx, dy in dirs:
                nx, ny = x + dx, y + dy
                if 0 <= nx < m and 0 <= ny < n and matrix[nx][ny] > matrix[x][y]:
                    ans = max(ans, dfs(nx, ny) + 1)
            mem[x][y] = ans
            return ans
        
        ans = 1
        for i in range(m):
            for j in range(n):
                ans = max(ans, dfs(i, j))
        return ans

习题4 路径条数 【荣耀】

https://oj.niumacode.com/problem/P1152

一张NxM的地图上每个点的海拔高度不同;从当前点只能访问上、下、左、右四个点中还没有到达过的点,且下一步选择的点的海拔高度必须高于当前点;求从地图中的点A到点B总的路径条数除以10^9的余数。地图左上角坐标为(0,0),右下角坐标为(N-1,M-1)
输入描述

第一行输入两个整数N,M(O<N≤600,0<M≤600)用空格隔开;

接下来N行输入,每行M个整数用空格隔开,代表对应位置的海拔高度(0<海拔高度≤360000);

最后一行四个整数x,y,z,w,前两个代表A的坐标为(x,y);后两个代表B的坐标为 (z,w);

输入保证A、B坐标不同,且坐标合法
输出描述

输出一个整数并换行,整数表示从A到B总的路径条数除以10^9的余数

示例1

输入

4 5
0 1 0 0 0
0 2 3 0 0
0 0 4 5 0
0 0 7 6 0
0 1 3 2

输出

2

思路与代码

从x,y出发搜索即可,过程可以使用记忆化搜索进行优化,避免重复运算。每次递归有4个方向,对于四个方向的总和求总和即可。

但是注意这里的状态依赖并不会成环,因为题目要求只能往高处走,所以不可能存在走回来的情况。

CPP

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;

const int MAX_N = 605;
const int MOD = 1000000007;

int n, m;
vector<vector<int>> matrix;
int dp[MAX_N][MAX_N];

// Directions: right, down, left, up
vector<pair<int, int>> dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

int dfs(int i, int j) {
    if (i == n - 1 && j == m - 1) {
        return 1;
    }
    
    if (dp[i][j] != -1) {
        return dp[i][j];
    }
    
    int count = 0;
    for (auto dir : dirs) {
        int ni = i + dir.first;
        int nj = j + dir.second;
        if (ni >= 0 && ni < n && nj >= 0 && nj < m && matrix[ni][nj] > matrix[i][j]) {
            count += dfs(ni, nj);
            count %= MOD;
        }
    }
    
    dp[i][j] = count;
    return count;
}

int main() {
    cin >> n >> m;
    matrix.resize(n, vector<int>(m));
    
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            cin >> matrix[i][j];
        }
    }
    
    // Initialize dp array with -1 (indicating not computed)
    memset(dp, -1, sizeof(dp));
    
    int startX, startY, endX, endY;
    cin >> startX >> startY >> endX >> endY;
    
    --startX; --startY; --endX; --endY;  // convert to 0-based index
    
    int result = dfs(startX, startY);
    cout << result << endl;
    
    return 0;
}

Java

import java.util.*;

public class Main {
    static final int MAX_N = 605;
    static final int MOD = 1000000007;
    static int n, m;
    static int[][] matrix;
    static int[][] dp;

    // Directions: right, down, left, up
    static int[][] dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

    static int dfs(int i, int j) {
        if (i == n - 1 && j == m - 1) {
            return 1;
        }
        
        if (dp[i][j] != -1) {
            return dp[i][j];
        }
        
        int count = 0;
        for (int[] dir : dirs) {
            int ni = i + dir[0];
            int nj = j + dir[1];
            if (ni >= 0 && ni < n && nj >= 0 && nj < m && matrix[ni][nj] > matrix[i][j]) {
                count += dfs(ni, nj);
                count %= MOD;
            }
        }
        
        dp[i][j] = count;
        return count;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        m = scanner.nextInt();
        matrix = new int[n][m];
        dp = new int[MAX_N][MAX_N];
        
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                matrix[i][j] = scanner.nextInt();
            }
        }
        
        // Initialize dp array with -1 (indicating not computed)
        for (int i = 0; i < MAX_N; ++i) {
            Arrays.fill(dp[i], -1);
        }
        
        int startX = scanner.nextInt() - 1;
        int startY = scanner.nextInt() - 1;
        int endX = scanner.nextInt() - 1;
        int endY = scanner.nextInt() - 1;
        
        int result = dfs(startX, startY);
        System.out.println(result);
        
        scanner.close();
    }
}

Python

n,m = map(int, input().split(" "))
matrix = []
for i in range(n):
    matrix.append([int(c) for c in input().split(" ")])

x,y,z,w = map(int, input().split(" "))
dirs = [[0,1],[1,0],[0,-1],[-1,0]]

dp = {}
def dfs(i,j):
    if i == z and j == w: return 1
    if (i,j) in dp: return dp[(i,j)]
    cur = 0
    for dir in dirs:
        ni, nj = i + dir[0], j + dir[1]
        if ni < 0 or nj < 0 or ni >= n or nj >= m or matrix[i][j] >= matrix[ni][nj]: continue
        cur += dfs(ni, nj)
    dp[(i,j)] = cur
    return cur

print(dfs(x,y))

六、常见陷阱

  1. 初始值容易填错,特别需要注意第一行和第一列。

  2. 需要注意是否存在依赖成环的问题,如果存在的话,可能要考虑其他算法,而不是一味dp。

0
博主关闭了所有页面的评论