LeetCode [329. 矩阵中的最长递增路径]

LeetCode [329. 矩阵中的最长递增路径]

题目

给定一个整数矩阵,找出最长递增路径的长度。

对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。

示例 1:

输入: nums =
[
[9,9,4],
[6,6,8],
[2,1,1]
]
输出: 4
解释: 最长递增路径为 [1, 2, 6, 9]。
示例 2:

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

该题也被称为滑雪问题。

思路:

  1. 状态表示方法

    f[i][j]:走到 i,j 时,路径的大小

  2. 状态转移:

    f[i][j] =max(f[i - 1][j] + 1, f[i][j - 1] + 1, f[i + 1][j] + 1, f[i][j + 1] + 1) 前提可以移动到 i,j

  3. 边界条件,遍历完所有可走路径

记忆化搜索:

​ 设一个辅助数组,记录已经经过的点。这样时间复杂度从指数级别降为 O(N^2)。在本题中可通过初始化状态矩阵 f 为特定值 -1,来使其具有记忆性。

​ 即 -1 是未走过的点,大于 0 为从此点走可以走的最大值

  1. 设好辅助数组,初始化全为 -1;
  2. 搜索路径通过递归(地图遍历),每次递归需要计算 4 个方向(上下左右)的情况(四个方位)
  3. 确定好是否为可行路径(地图的边界)和题目要求只能递增走,符合条件继续递归,不符合条件返回
  4. 每走一步(进入一次递归),进行 +1
  5. 和状态矩阵中的数值进行比较,选择最大值
  6. 递归完成,得到最优解

代码:

class Solution {
public:
    vector<vector<int>> f;
    int n, m;
    // 记忆化搜索
   
    int dx[4] = {-1, 0, 1, 0};
    int dy[4] = {0, 1, 0, -1};
    int dp(int x, int y, vector<vector<int>>& matrix)		// 定义到 x,y 时的状态(路径大小)
    {
        if(f[x][y] != -1) return f[x][y];
        f[x][y] = 1;
        for(int i = 0; i < 4; i++)
        {
            int a = x + dx[i], b = y + dy[i];
            if(a >= 0 && a < n && b >= 0 && b < m && matrix[a][b] > matrix[x][y])
                f[x][y] = max(f[x][y], dp(a, b, matrix) + 1);
        }
        return f[x][y];
    }

    int longestIncreasingPath(vector<vector<int>>& matrix) {
        if(matrix.empty())  return 0;
        n = matrix.size(); m = matrix[0].size();
        f = vector<vector<int>> (n, vector<int>(m, -1));
        int res = 0;
        for(int i = 0; i < n; i++)
            for(int j = 0; j < m; j++)
                res = max(res, dp(i, j, matrix));
        return res;
    }
};

解析:

以输入为

{ {9, 9, 4}, {6, 6, 8 }, {2, 1, 1} } 为例

matrixj = 0j = 1j = 2
i = 0994
i = 1668
i = 2211

程序从(0,0)开始遍历。每次以 “上 右 下 左” 进行尝试

fj = 0j = 1j = 2
i = 0-1-1-1
i = 1-1-1-1
i = 2-1-1-1
程序入口 
for(int i = 0; i < n; i++)
	for(int j = 0; j < m; j++)
		res = max(res, dp(i, j, matrix));

进入第一个递归,即 从(0,0)开始,只有 “右 下”,可以走,但是数值并非递增,

所以,在位置(0,0)上,最多只能走一步。

更新状态方程

fj = 0j = 1j = 2
i = 01-1-1
i = 1-1-1-1
i = 2-1-1-1
四个方向移动
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};

状态转移
for(int i = 0; i < 4; i++)
{
    int a = x + dx[i], b = y + dy[i];
    if(a >= 0 && a < n && b >= 0 && b < m && matrix[a][b] > matrix[x][y])
    	f[x][y] = max(f[x][y], dp(a, b, matrix) + 1);
}

从状态转移条件可以看出此处需要将 matrix 传入 dp 函数中,因为每一次需要比较大小

程序进行执行,直到位置达到(0,2),4,直观上我们可以看出,向左(9)和向下(8)都可以走。

程序在状态转移中也如此

  1. 向上和向右,均出地图边界
  2. 向下(8),满足所有条件,第一层递归,将(1,2)位置传入dp函数
    1. f(1,2) = -1,(1,2)没走过
    2. 在此点至少为路径长度为 1,f(1,2) = 1
    3. 在(1,2)四个方向中,没有可走的点,所以在此点最大可以走 1 步
    4. 返回 1
    5. f[x][y] = max(f[x][y], dp(a, b, matrix) + 1); ===》f[x][y] = max(1, 1+1);
  3. 向左(9),满足所有条件,第一层递归,将(0,1)位置传入dp函数
    1. f(0,1) = 1,没走过,直接返回该点的值(已经被更新为 1)
    2. f[x][y] = max(f[x][y], dp(a, b, matrix) + 1); ===》f[x][y] = max(2, 1+1);
  4. 最后 (0,2)更新为 2
fj = 0j = 1j = 2
i = 0112
i = 1-1-1-1
i = 2-1-1-1

以此类推,如果起始从(2,1)点开始怎会进行多层递归处理,每次返回也会将经过路径中的每点的最长递增路径返回。如果从以(0,0,)位置走,也会一点一点将数值填充好

最后结果

fj = 0j = 1j = 2
i = 0112
i = 1221
i = 2342
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值