BM61 矩阵最长递增路径
知识点dfs动态规划图
描述
给定一个 n 行 m 列矩阵 matrix ,矩阵内所有数均为非负整数。 你需要在矩阵中找到一条最长路径,使这条路径上的元素是递增的。并输出这条最长路径的长度。这个路径必须满足以下条件:
1. 对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外。
2. 你不能走重复的单元格。即每个格子最多只能走一次。
数据范围:,进阶:空间复杂度 ,时间复杂度
例如:当输入为[[1,2,3],[4,5,6],[7,8,9]]时,对应的输出为5,其中的一条最长递增路径如下图所示:
示例1
输入:
[[1,2,3],[4,5,6],[7,8,9]]
复制返回值:
5
复制说明:
1->2->3->6->9即可。当然这种递增路径不是唯一的。
示例2
输入:
[[1,2],[4,3]]
复制返回值:
4
复制说明:
1->2->3->4
题解
方法一:暴力dfs+辅助数组
思路:
- 使用一个辅助数组v[i][k]来表示遍历过程中是否已经访问过,0表示没有访问过,1表示访问过
- 遍历整个矩阵,选取任意的mat[i][k]为七点,往四个方向递归访问
- 返回最大长度
注意:
这个解法时间超了~~
代码如下:
using namespace std;
// https://www.nowcoder.com/practice/7a71a88cdf294ce6bdf54c899be967a2?tpId=295&tqId=1076860&ru=/exam/oj&qru=/ta/format-top101/question-ranking&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26tab%3D%25E7%25AE%2597%25E6%25B3%2595%25E7%25AF%2587%26topicId%3D295
// BM61 矩阵最长递增路径
int find_path(vector<vector<int>> &matrix, int i, int k, std::vector<std::vector<int>> &v)
{
int len = 1;
v[i][k] = 1; // 表示i,k位置已经被访问
int value = matrix[i][k];
if (i > 0 && matrix[i - 1][k] > value && v[i - 1][k] == 0)
{
v[i - 1][k] = 1;
len = std::max(len, 1 + find_path(matrix, i - 1, k, v));
v[i - 1][k] = 0;
}
if (i < matrix.size() - 1 && matrix[i + 1][k] > value && v[i + 1][k] == 0)
{
v[i + 1][k] = 1;
len = std::max(len, 1 + find_path(matrix, i + 1, k, v));
v[i + 1][k] = 0;
}
if (k > 0 && matrix[i][k - 1] > value && v[i][k - 1] == 0)
{
v[i][k - 1] = 1;
len = std::max(len, 1 + find_path(matrix, i, k - 1, v));
v[i][k - 1] = 0;
}
if (k < matrix[0].size() - 1 && matrix[i][k + 1] > value && v[i][k + 1] == 0)
{
v[i][k + 1] = 1;
len = std::max(len, 1 + find_path(matrix, i, k + 1, v));
v[i][k + 1] = 0;
}
return len;
}
int solve(vector<vector<int>> &matrix)
{
if (matrix.size() == 0 || matrix[0].size() == 0)
{
return 0;
}
int max_length = 0;
for (int i = 0; i < matrix.size(); ++i)
{
for (int k = 0; k < matrix[0].size(); ++k)
{
std::vector<std::vector<int>> v(matrix.size(), std::vector<int>(matrix[0].size(), 0));
max_length = std::max(find_path(matrix, i, k, v), max_length);
}
}
return max_length;
}
解法二:暴力dfs的优化
由于使用了辅助数组,每次遍历的时候都需要分配内存,消耗比较大。可不可以省略掉这个辅助数组呢?答案是可以的。辅助数组只不过是在访问了mat[i][k]位置后将其标记,防止以后再访问。但是由于矩阵中的元素是有大小关系的,对于i、k任意相邻的一个位置,它的值要么大于它,要么小于等于它,即使重复计算也由于有大小关系的存在不会导致重复递归。只需要在进入下次递归前,将当前访问的值传入下次递归即可。
我们直接暴力枚举每个起点进行DFS,在过程中维护最大值即可。递归直接往四个方向进行即可,但是首先需要先判断四个方向的位置是否是可行的,也就是是否会出现越界的情况。
代码如下:
int dfs(vector<vector<int>> &matrix, int i, int k, int pre_value)
{
int value = matrix[i][k];
if (value <= pre_value) // 当前要访问的值,小于前面的节点,表示当前路径长度为0
{
return 0;
}
int len = 0;
if (i > 0)
{
len = std::max(len, dfs(matrix, i - 1, k, value));
}
if (i < matrix.size() - 1)
{
len = std::max(len, dfs(matrix, i + 1, k, value));
}
if (k > 0)
{
len = std::max(len, dfs(matrix, i, k - 1, value));
}
if (k < matrix[0].size() - 1)
{
len = std::max(len, dfs(matrix, i, k + 1, value));
}
return len + 1;
}
int solve(vector<vector<int>> &matrix)
{
if (matrix.size() == 0 || matrix[0].size() == 0)
{
return 0;
}
int max_length = 0;
for (int i = 0; i < matrix.size(); ++i)
{
for (int k = 0; k < matrix[0].size(); ++k)
{
max_length = std::max(dfs(matrix, i, k, -1), max_length);
}
}
return max_length;
}
解法三:dfs+记忆话搜索
解法一中,我们使用了一个辅助数组来记录路径是否已经被访问过,使用的是0和1来表示的。这导致每次搜索的时候本来已经搜索过的路径要重复的计算。如果我们使用这个辅助数组将已经计算过的结果保存起来,可以减少大量的计算。逻辑还是和解法一一样,只是将辅助数组的功能改变了。当辅助数组的值不为-1时表示已经搜索过这个路径,直接返回即可。
代码如下:
int memory_search(vector<vector<int>> &matrix, int i, int k, std::vector<std::vector<int>> &v)
{
if (v[i][k] != -1)
{
return v[i][k];
}
int len = 1;
int value = matrix[i][k];
if (i > 0 && matrix[i - 1][k] > value)
{
len = std::max(len, 1 + memory_search(matrix, i - 1, k, v));
}
if (i < matrix.size() - 1 && matrix[i + 1][k] > value)
{
len = std::max(len, 1 + memory_search(matrix, i + 1, k, v));
}
if (k > 0 && matrix[i][k - 1] > value)
{
len = std::max(len, 1 + memory_search(matrix, i, k - 1, v));
}
if (k < matrix[0].size() - 1 && matrix[i][k + 1] > value)
{
len = std::max(len, 1 + memory_search(matrix, i, k + 1, v));
}
v[i][k] = len;
return len;
}
int solve(vector<vector<int>> &matrix)
{
if (matrix.size() == 0 || matrix[0].size() == 0)
{
return 0;
}
int max_length = 0;
std::vector<std::vector<int>> v(matrix.size(), std::vector<int>(matrix[0].size(), -1));
for (int i = 0; i < matrix.size(); ++i)
{
for (int k = 0; k < matrix[0].size(); ++k)
{
max_length = std::max(memory_search(matrix, i, k, v), max_length);
}
}
return max_length;
}