题目描述
给定一个 n 行 m 列矩阵 matrix ,矩阵内所有数均为非负整数。 你需要在矩阵中找到一条最长路径,使这条路径上的元素是递增的。并输出这条最长路径的长度。
这个路径必须满足以下条件:
1. 对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外。
2. 你不能走重复的单元格。即每个格子最多只能走一次。
数据范围:1≤n,m≤1000,0≤matrix[i][j]≤1000
要求:空间复杂度 O(nm)O(nm) ,时间复杂度 O(nm)O(nm)
例如:当输入为[[1,2,3],[4,5,6],[7,8,9]]时,对应的输出为5, 其中的一条最长递增路径如下图所示:
![](https://img-blog.csdnimg.cn/img_convert/05c254f87bceb5762b97120417456390.png)
深度优先搜索
核心代码
vector<vector<int>> dp;
int Max=0,n,m;
array<int,4> x={1,-1,0,0};
array<int,4> y={0,0,1,-1};
int dfs(vector<vector<int>>& matrix,int row,int col)
{
dp[row][col]=1;
for(int i=0;i<4;++i){
int r=row+x[i];
int c=col+y[i];
if(r<n&&r>=0&&c<m&&c>=0&&matrix[row][col]<matrix[r][c]){
if(!dp[r][c]) dp[r][c]=dfs(matrix,r,c);
dp[row][col]=max(dp[row][col],dp[r][c]+1);
}
}
Max=max(Max,dp[row][col]);
return dp[row][col];
}
int solve(vector<vector<int> >& matrix) {
n=matrix.size();
m=matrix[0].size();
dp=vector<vector<int>>(n,vector<int>(m,0));
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
if(!dp[i][j]) dfs(matrix,i,j);
}
}
return Max;
}
典型搜索题,又有点动态规划的味道。注意学习其中的代码tip:
对于这种向四个方向搜索的题目,可用方向数组(两个一维或一个二维)表示每次方向的变化,这样可省去大量重复代码;
在函数间传递vector,最好传递& vector,这样避免了复制的时间,当vector规模很大时,可大大缩短时间(本题就是将dfs参数列表中matrix修改为引用之后才AC的);
创建含指定个数重复元素的vector可参考如下代码:dp=vector<vector<int>>(n,vector<int>(m,0));
更新最值可采用max,min函数;
Max要初始化为0。不要以为简单的小细节不值一提。
广度优先搜索
思路:
我们可以将矩阵看成是一个有向图,一个元素到另一个元素递增,代表有向图的箭头。这样我们可以根据有向图的出度入度找到最长的路径,且这个路径在矩阵中就是递增的。
具体做法:
step 1:计算每个节点(单元格)所对应的出度(符合边界条件且递增),对于作为边界条件的单元格,它的值比所有的相邻单元格的值都要大,因此作为边界条件的单元格的出度都为0。利用一个二维矩阵记录每个单元格的出度
step 2:利用拓扑排序的思想,从所有出度为0的单元格开始进行广度优先搜索。
step 3:借助队列来广度优先搜索,队列中每次加入出度为0的点,即路径最远点,每次从A点到B点,便将A点出度减一。
step 4:每次搜索都会遍历当前层的所有单元格,更新其余单元格的出度,并将出度变为0的单元格加入下一层搜索。
step 5:当搜索结束时,搜索的总层数即为矩阵中的最长递增路径的长度,因为bfs的层数就是路径增长的层数。
核心代码
//记录四个方向
int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int n, m;
int solve(vector<vector<int> >& matrix) {
//空矩阵
if (matrix.size() == 0 || matrix[0].size() == 0)
return 0;
n = matrix.size();
m = matrix[0].size();
//记录每个单元的出度
vector<vector<int> > outdegrees(n, vector<int> (m));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
for (int k = 0; k < 4; k++) {
int nexti = i + dirs[k][0];
int nextj = j + dirs[k][1];
if (nexti >= 0 && nexti < n && nextj >= 0 && nextj < m && matrix[nexti][nextj] > matrix[i][j]) {
//符合条件,记录出度
outdegrees[i][j]++;
}
}
}
}
queue <pair<int, int> > q;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
if (outdegrees[i][j] == 0)
//找到出度为0的入队列
q.push({i, j});
int res = 0;
while (!q.empty()) {
res++;
int size = q.size();
for (int x = 0; x < size; x++) {
pair<int, int> temp = q.front();
q.pop();
int i = temp.first;
int j = temp.second;
//四个方向
for (int k = 0; k < 4; k++) {
int nexti = i + dirs[k][0];
int nextj = j + dirs[k][1];
//逆向搜索,所以下一步是小于
if (nexti >= 0 && nexti < n && nextj >= 0 && nextj < m && matrix[nexti][nextj] < matrix[i][j]) {
//符合条件,出度递减
outdegrees[nexti][nextj]--;
if (outdegrees[nexti][nextj] == 0) {
q.push({nexti, nextj});
}
}
}
}
}
return res;
}