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包装写也行

相关推荐
AI小老六6 小时前
SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent
后端·算法·ai编程
胡萝卜术7 小时前
从“分数打架”到“排名投票”:为什么你的ChatBI必须用RRF?
算法·设计模式·面试
Asize7 小时前
初识DFS 与 BFS:递归、队列与图遍历
算法
罗西的思考21 小时前
机器人 / 强化学习】HIL-SERL:人类在环驱动的具身智能进化框架
人工智能·算法·机器学习
美团技术团队1 天前
LongCat 开源 VitaBench 2.0:长期动态智能体基准新标杆
人工智能·算法
用户805533698031 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
To_OC2 天前
LC 207 课程表:刚学图论那会儿,我连这是拓扑排序都没看出来
javascript·算法·leetcode
To_OC2 天前
LC 208 实现 Trie 前缀树:曾被名字劝退,写完发现是送分题
javascript·算法·leetcode
BadBadBad__AK2 天前
线段树维护区间 k 次方和
c++·数学·算法·stl