8.27 网格memo

lc329

计算矩阵中最长递增路径长度

尝试从矩阵每个位置出发,int dfs() 往上下左右四个方向找严格递增的路径

ret=max(ret,dfs(x,y)+1);

return memoij=ret;

返回所有路径里的最长长度

class Solution

{

public:

int dx4={0,0,1,-1};

int dy4={1,-1,0,0};

int m,n;

vector<vector<int>> memo;

vector<vector<int>> matrix;

int longestIncreasingPath(vector<vector<int>>& matrix)

{

this->matrix=matrix;

m=matrix.size();

n=matrix0.size();

int cnt=0;

memo.resize(m,vector<int>(n,0));

for(int i=0;i<m;i++)

{

for(int j=0;j<n;j++)

{

cnt=max(cnt,dfs(i,j));

}

}

return cnt;

}

int dfs(int i,int j)

{

if(memoij) return memoij;

int ret=1; //init

for(int k=0;k<4;k++)

{

int x=i+dxk,y=j+dyk;

if(x>=0 && x<m && y>=0 && y<n

&& matrixxy>matrixij)

{

++ret=max(ret,dfs(x,y)+1);++

++}++

++}++

++return memoij=ret;++

}

};

lc3459

int dfs 沿着某个方向一步步探路,符合规则(++值对、没越界)++就接着走,还能处理 "转一次弯" 的情况

同时用 memo 避免重复计算,(memoijk: 从++(i,j) 位置往第 k 个方向走的最长有效长度++)

  1. 整体思路

代码要在一个由 0、1、2 组成的二维矩阵里,找符合 "V 形对角线段" 规则的最长线段。

核心逻辑:从每个值为 1 的位置出发,沿着四个可能的对角方向(比如↘、↙、↖、↗ 这类斜线方向)去探索,++按照 2、0、2、0...... 的序列模式走,还能最多右转一次 90 度弯,++最后找出所有情况里最长的线段长度。

class Solution {

static constexpr int DIRS42 = {{1, 1}, {1, -1}, {-1, -1}, {-1, 1}};

public:

int lenOfVDiagonal(vector<vector<int>>& grid) {

int m = grid.size(), n = grid0.size();

vector memo(m, vector<array<int, 4>>(n));

auto dfs = \&(this auto&& dfs, int i, int j, int k, bool can_turn, int target) -> int

{

i += DIRSk0;

j += DIRSk1;

++if (i < 0 || i >= m || j < 0 || j >= n || gridij != target) {
return 0;
}++

// 只++在 can_turn=false 时读取和写入 memo++

if (!can_turn && memoijk) {

return memoijk;

}

++int res = dfs(i, j, k, can_turn, 2 - target) + 1; //延此方向往下走++

++if (!can_turn) {
return memoijk = res;
}
++

int maxs4 = {m - i, j + 1, i + 1, n - j}; // 理论最大值(走到底)

++k = (k + 1) % 4;//顺时针++

++// 优化二:如果理论最大值没有超过 res,那么不递归++

if (min(maxsk, maxs(k + 3) % 4) > res) {

res = max(res, dfs(i, j, k, false, 2 - target) + 1); //尝试转后,还是维护最大

}

++return res;++

};

int ans = 0;

for (int i = 0; i < m; i++) {

for (int j = 0; j < n; j++) {

if (gridij != 1)

continue;

int maxs4 = {m - i, j + 1, i + 1, n - j}; // 理论最大值(走到底)

for (int k = 0; k < 4; k++) {

// 优化一:如果理论最大值没有超过 ans,那么不递归

if (maxsk > ans) {

ans = max(ans, dfs(i, j, k, true, 2) + 1);

}

}

}

}

return ans;

}

};

  1. 解释

(1)方向定义

static constexpr int DIRS42 = {{1, 1}, {1, -1}, {-1, -1}, {-1, 1}};

预先定义好的 4 种对角移动方向

(2)记忆化搜索( memo 数组)

vector memo(m, vector<array<int, 4>>(n));

//memo 是用来 缓存已经计算过的结果 ,避免重复计算。比如从++(i,j) 位置往第 k 个方向走的最长有效长度,++算过一次就存起来,下次再碰到就直接用,能提升效率。

(3)深度优先搜索( dfs 函数)

auto dfs = \&(this auto&& dfs, int i, int j, int k, bool can_turn, int target) -> int {

// 先沿着当前方向走一步,更新坐标

i += DIRSk0;

j += DIRSk1;

if (i < 0 || i >= m || j < 0 || j >= n || gridij != target) {

return 0;

}

// 如果还不能转弯(can_turn=false),看看缓存里有没有结果,有的话直接返回

if (!can_turn && memoijk) {

return memoijk;

}

// 能走到这,说明当前格子有效,接着往下一步找,递归调用 dfs,同时切换下一个要找的 target(2 变 0,0 变 2 )

int res = dfs(i, j, k, can_turn, 2 - target) + 1;

// 如果还不能转弯,就把当前算出来的结果存到 memo 里,方便后续复用

if (!can_turn) {

return memoijk = res;

}

// 下面是处理 "可以转弯" 的情况

int maxs4 = {m - i, j + 1, i + 1, n - j};

// 切换方向(右转 90 度,对应 k = (k + 1) % 4 )

k = (k + 1) % 4;

// 优化:如果理论上转完弯能走的长度,还没当前 res 长,就不递归了,省点时间

if (min(maxsk, maxs(k + 3) % 4) > res) {

// 转弯后,不能再转了(can_turn 设为 false ),递归计算新方向的长度,然后和当前 res 比,取大的

res = max(res, dfs(i, j, k, false, 2 - target) + 1);

}

return res;

};

简单说

dfs 沿着某个方向一步步探路,符合规则(值对、没越界)就接着走,还能处理 "转一次弯" 的情况

同时用 memo 避免重复计算,(memoijk: 从++(i,j) 位置往第 k 个方向走的最长有效长度++)

(4)主逻辑遍历

int ans = 0;

for (int i = 0; i < m; i++) {

for (int j = 0; j < n; j++) {

// 只从值为 1 的位置开始找,因为题目说线段从 1 开始

if (gridij != 1) {

continue;

}

// 计算从 (i,j) 出发,四个方向理论上能走的最大长度(走到边界的长度)

int maxs4 = {m - i, j + 1, i + 1, n - j};

for (int k = 0; k < 4; k++)

{

// ++优化:如果理论长度都没当前 ans 大,没必要递归,跳过++

if (maxsk > ans) {

++// 从 (i,j) 出发,第 k 个方向,允许转弯(can_turn=true),第一个要找的 target 是 2++

ans = max(ans, dfs(i, j, k, true, 2) + 1);

}

}

}

}

return ans;

遍历整个矩阵 ,找到所有值为 1 的位置,然后对每个 1 的位置,尝试++四个对角方向出发去探索最长 V 形线段,++不断更新 ans (记录全局最长长度 )

auto dfs = \&(this auto&& dfs, int i, int j, int k, bool can_turn, int target) -> int

{

// ... 函数体

};

递归lambda的写法,显式捕获当前类的 this 指针,在 Lambda 内部访问类的成员。

用function包装写也行

相关推荐
Mem0rin6 分钟前
二分查找:左右边界
数据结构·算法
鸿芯微控科技23 分钟前
MFC关断后还有流量怎么办?零流量、阀门泄漏、压差与Python分析
c++·python·mfc·质量流量控制器·关断泄漏·零流量测试
大明者省10 小时前
WSL2 Ubuntu22.04 GPU训练环境配置指南
人工智能·算法·计算机视觉
白狐_79811 小时前
408数据结构第8章:排序②——性质对比秒杀、场景选择与外部排序
java·数据结构·算法
zander25812 小时前
LeetCode 84:柱状图中的最大矩形——单调栈如何确定左右边界
java·数据结构·算法
ShineWinsu12 小时前
对于C++:C++11中lambda、function、bind的解析
c++·面试·笔试·开发·lambda·bind·function
码匠许师傅12 小时前
【C++ 面试真题】聊聊 C++ 的拷贝构造与拷贝赋值
java·c++·面试
Shell运维手记12 小时前
Linux 常用基础命令学习笔记
linux·运维·笔记·学习·算法·github
杨航 AI12 小时前
O(n log n):线性对数原理拆解 这个是排序算法的黄金复杂度之一。
算法·排序算法
船厂电气自动化ai大模型13 小时前
AI大模型与数学 第32课 函数凹凸性与二阶导数:拐点求解、凹凸区间计算(10道二阶导数计算题)
数据结构·人工智能·python·深度学习·算法