当前位置: 首页 > news >正文

LeetCode算法日记 - Day 62: 黄金矿工、不同路径III

目录

1. 黄金矿工

1.1 题目解析

1.2 解法

1.3 代码实现

2. 不同路径III

2.1 题目解析

2.2 解法

2.3 代码实现


1. 黄金矿工

https://leetcode.cn/problems/path-with-maximum-gold/

你要开发一座金矿,地质勘测学家已经探明了这座金矿中的资源分布,并用大小为 m * n 的网格 grid 进行了标注。每个单元格中的整数就表示这一单元格中的黄金数量;如果该单元格是空的,那么就是 0

为了使收益最大化,矿工需要按以下规则来开采黄金:

  • 每当矿工进入一个单元,就会收集该单元格中的所有黄金。
  • 矿工每次可以从当前位置向上下左右四个方向走。
  • 每个单元格只能被开采(进入)一次。
  • 不得开采(进入)黄金数目为 0 的单元格。
  • 矿工可以从网格中 任意一个 有黄金的单元格出发或者是停止。

示例 1:

输入:grid = [[0,6,0],[5,8,7],[0,9,0]]
输出:24
解释:
[[0,6,0],[5,8,7],[0,9,0]]
一种收集最多黄金的路线是:9 -> 8 -> 7。

示例 2:

输入:grid = [[1,0,7],[2,0,6],[3,4,5],[0,3,0],[9,0,20]]
输出:28
解释:
[[1,0,7],[2,0,6],[3,4,5],[0,3,0],[9,0,20]]
一种收集最多黄金的路线是:1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7。

提示:

  • 1 <= grid.length, grid[i].length <= 15
  • 0 <= grid[i][j] <= 100
  • 最多 25 个单元格中有黄金。

1.1 题目解析

题目本质
在一个最多 15×15 的网格里找一条“简单路径”(不重复格子),路径上格子权值为黄金数,目标是让路径和最大。属于典型的位置驱动 DFS + 回溯 + 局部最优扩展问题。

常规解法
从任意有金子的格子出发,四向扩展,每走进一个格子就把该格子的金子加入路径和,并标记已访问;探索完四个方向后回退(回溯),恢复标记,继续尝试其它方向;主过程对所有非零格子作为起点取最大值。

问题分析
若不回溯或不做访问标记,会反复走回头路甚至产生环;若只从边界起步会错过中部最优路径。最坏情况下分支因子≤4、深度≤25(最多 25 个含金格),搜索上界可接受,但剪枝(遇 0/越界/已访问立即跳过)非常关键。

思路转折
要想稳且高效:

  • 起点必须遍历所有非零格子(题意允许从任意含金格起/止)。

  • 递归返回值定义为“从该格出发能获得的最大金子”,则状态转移是:当前格子金子 + 四邻递归收益的最大值。

  • 成对回溯:进入邻格前置 vis=true,子分支失败后恢复 false,确保路径不复用格子。

1.2 解法

算法思想:

  • 设 dfs(i,j) 为从 (i,j) 出发能获得的最大金子,则 dfs(i,j) = grid[i][j] + max( dfs(x,y) ),其中 (x,y) 是四邻中未访问且 grid[x][y]>0 的合法邻居;若无合法邻居则为 grid[i][j]。

  • 遍历每个非零格子作为起点:标记→调用 dfs→更新答案→恢复标记。

  • 回溯保证每条路径不重复格子,穷举所有可能路径。

i)取 m,n,初始化 vis[m][n]=false。

ii)双层循环遍历每个格子 (i,j):

  • grid[i][j]==0 跳过;

  • 将 vis[i][j]=true,计算 cur=dfs(i,j);

  • 更新 ret=max(ret,cur);将 vis[i][j]=false。

iii)dfs(si,sj):

  • 设 bestNext=0;

  • 枚举四方向 (x,y),若在界内、未访问、且 grid[x][y]>0:

    • 回溯保证每条路径不重复格子,穷举所有可能路径

    • 置 vis[x][y]=true;

    • 计算 gain=dfs(x,y) 并更新 bestNext=max(bestNext,gain);

    • 回溯 vis[x][y]=false;

  • 返回 grid[si][sj] + bestNext。

iv)返回全局最大值 ret。

易错点:

  • 只从边界起点搜索会漏解;必须遍历所有非零起点。

  • 忘记接住子递归的返回值、或没有对四邻取最大值,会导致只加当前格金子。

  • 回溯不对称(标记后未恢复)会影响后续路径。

  • 访问标记要在进入邻格前置位、在该分支结束后恢复。

  • 不要走进 grid==0 的格子(题意禁止)。

1.3 代码实现

class Solution {boolean[][] vis;int m,n;int[] dx = {1,-1,0,0};int[] dy = {0,0,1,-1};public int getMaximumGold(int[][] grid) {m = grid.length;n = grid[0].length;vis = new boolean[m][n];int ret = 0;for(int i = 0; i < m; i++){for(int j = 0; j < n; j++){if(grid[i][j] != 0){              // 任意非零格都可作为起点vis[i][j] = true;int cur = dfs(grid, i, j);    // 从该点出发的最大收益vis[i][j] = false;ret = Math.max(ret, cur);}}}return ret;}public int dfs(int[][] grid, int si, int sj){int bestNext = 0;                         // 记录四邻的最佳后续收益for(int k = 0; k < 4; k++){int x = si + dx[k], y = sj + dy[k];if(x>=0 && x<m && y>=0 && y<n && !vis[x][y] && grid[x][y] != 0){vis[x][y] = true;int gain = dfs(grid, x, y);      // 接住子递归返回值vis[x][y] = false;if(gain > bestNext) bestNext = gain;}}return grid[si][sj] + bestNext;           // 当前格金子 + 最佳后续}
}

复杂度分析

  • 时间复杂度:起点最多 25 个,每条路径深度≤25、分支因子≤4,最坏可视作 O(25 * 4^25) 上界;但网格小、含金格有限且有强剪枝(越界/0/已访问跳过),实际远小于上界。

  • 空间复杂度:O(25) 递归栈深度 + O(mn) 访问标记布尔表。

2. 不同路径III

https://leetcode.cn/problems/unique-paths-iii/

在二维网格 grid 上,有 4 种类型的方格:

  • 1 表示起始方格。且只有一个起始方格。
  • 2 表示结束方格,且只有一个结束方格。
  • 0 表示我们可以走过的空方格。
  • -1 表示我们无法跨越的障碍。

返回在四个方向(上、下、左、右)上行走时,从起始方格到结束方格的不同路径的数目

每一个无障碍方格都要通过一次,但是一条路径中不能重复通过同一个方格

示例 1:

输入:[[1,0,0,0],[0,0,0,0],[0,0,2,-1]]
输出:2
解释:我们有以下两条路径:
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2)
2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)

示例 2:

输入:[[1,0,0,0],[0,0,0,0],[0,0,0,2]]
输出:4
解释:我们有以下四条路径: 
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3)
2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3)
3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3)
4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)

示例 3:

输入:[[0,1],[2,0]]
输出:0
解释:
没有一条路能完全穿过每一个空的方格一次。
请注意,起始和结束方格可以位于网格中的任意位置。

提示:

  • 1 <= grid.length * grid[0].length <= 20

2.1 题目解析

题目本质
在网格上寻找一条从起点 1 到终点 2 的“哈密顿式”路径(仅覆盖所有非障碍格一次),统计这样的路径条数。典型位置驱动 DFS + 回溯 + 计数问题。

常规解法
从起点出发,四向扩展;每走到一个格子就标记为已访问,遇到终点时判断是否恰好走遍了所有可走格;失败则回退继续试其它方向。

问题分析
若不到终点就计数、或到终点后继续扩展,都会导致错误计数;若不做访问标记,会重复走格。状态空间最坏可看作分支因子 ≤ 4,路径长度 ≤ 非障碍格数(≤ 20),枚举仍可接受。

思路转折

  • 要想正确:

    • 终点处必须校验“是否已覆盖全部非障碍格”,否则会把未覆盖全的路径误算进去。

    • 到达终点后立即返回,不能继续向外扩展。

    • 起点进入前先标记已访问,否则可能回到起点重复走格。

  • 要想稳:预先统计 0 的数量为 step,到达 2 时用“实际步数 count 是否等于 step+1”判断是否覆盖完(起点到终点共 step+1 步)。

2.2 解法

算法思想:

  • 预处理:统计空格数量 step = #zeros。

  • 回溯搜索:dfs(x,y,count) 表示当前站在 (x,y),已走了 count 步;

    • 到达终点 2 时,若 count == step+1(覆盖了所有非障碍格),答案 ret++;随后立即返回。

    • 否则在 4 个方向上对合法未访问且非障碍格递归,进栈标记、出栈恢复。

  • 启动:从起点 1 开始,先标记已访问,再调用 dfs(1 的坐标, 0)。

i)统计网格尺寸 m,n,分配 vis[m][n]。

ii)遍历网格计数 step = 空格(0) 的数量。

iii)找到起点 (sx,sy),将 vis[sx][sy]=true,调用 dfs(sx,sy,0),回来后恢复标记。

iv)dfs 中若 grid[x][y]==2:计算 need = step+1,若 count==need 则答案加一,并返回

v)否则枚举 4 邻 (nx,ny):越界/障碍/已访问跳过;合法则标记→递归 count+1→恢复。

vi)返回最终计数 ret。

易错点:

  • 终点判断必须在递归入口先做,并立刻返回

  • 起点必须在调用 dfs 之前标记为已访问。

  • count 是“步数(边数)”,覆盖所有非障碍格需要的步数是 #zeros + 1。

  • 回溯要对称:进入前标记、返回后恢复。

2.3 代码实现

class Solution {boolean[][] vis;int m,n;int[] dx = {1,-1,0,0};int[] dy = {0,0,1,-1};int step; // 空格(0) 的数量int ret;  // 路径计数public int uniquePathsIII(int[][] grid) {// 防御性初始化step = 0; ret  = 0;m = grid.length;n = grid[0].length;vis = new boolean[m][n];// 统计所有 0 的数量for(int i = 0; i < m; i++){for(int j = 0; j < n; j++){if(grid[i][j] == 0) step++;}}// 从起点 1 出发for(int i = 0; i < m; i++){for(int j = 0; j < n; j++){if(grid[i][j] == 1) {vis[i][j] = true;           // 起点先标记dfs(grid, i, j, 0);         // 已走 0 步vis[i][j] = false;          // 回溯恢复}}}return ret;}public void dfs(int[][] g, int si, int sj, int count){// 到达终点:判断是否覆盖所有非障碍格(0 与 1 与 2)if(g[si][sj] == 2){int need = step + 1;                // 从 1 到 2 共需走的步数if(count == need) ret++;            // 覆盖完才计数return;                             // 终点必须停止}for(int k = 0; k < 4; k++){int x = si + dx[k], y = sj + dy[k];if(x>=0 && x<m && y>=0 && y<n && !vis[x][y] && g[x][y] != -1){vis[x][y] = true;dfs(g, x, y, count + 1);vis[x][y] = false;}}}
}

复杂度分析:

  • 时间复杂度:最坏在非障碍格数 K ≤ 20 的状态空间上做回溯,枚举所有不重复路径,复杂度上界接近 O(4^K)(实际远小于上界,因越界/障碍/已访问强剪枝)。

  • 空间复杂度:O(K) 递归栈 + O(mn) 访问标记。

http://www.dtcms.com/a/442087.html

相关文章:

  • 济南建设工程信息网站asp.net实用网站开发
  • deepseek 的对话json导出成word和pdf
  • php 网站 项目如何用wordpress搭建个人博客
  • Prometheus监控K8S集群-ExternalName-endpoints-ElasticStack采集K8S集群日志实战
  • 解读DeepSeek-V3.2-Exp:基于MLA架构的Lightning Index如何重塑长上下文效率
  • 视频网站开发公司有哪些公司国家新闻出版
  • GitHub 热榜项目 - 日榜(2025-10-04)
  • datawhale RAG技术全栈指南 202509 第6次作业
  • 电影网站建设成本百怎么做网站
  • e语言可以做网站吗西安网站建设 翼驰
  • Redis 热点数据与冷数据解析
  • 【计算机视觉】车牌分割定位识别
  • wordpress做网站容易吗用lls建设一个网站
  • 从 3.6 亿订单表到毫秒级查询:分库分表指南
  • 网站怎样设计网页做黄金期货的网站
  • 无线网卡——WIFI7无法在Ubuntu22.04系统中使用
  • Ubuntu20.04下的Pytorch2.7.1安装
  • MySQL:C语言链接
  • 合肥市门户网站中国纪检监察报社长
  • 黑马点评秒杀优化和场景补充
  • 嵌入式硬件——基于IMX6ULL的UART(通用异步收发传输器)
  • Spark Shuffle:分布式计算的数据重分布艺术
  • 网站能看出建设时间吗网页设计工资统计
  • Postgres数据库truncate表无有效备份恢复---惜分飞
  • 【邪修玩法】如何在WPF中开放 RESTful API 服务
  • 开源 C++ QT QML 开发(二)工程结构
  • 2025生成式AI部署避坑指南:芯片卡脖子与依赖链爆炸的实战解决方案
  • 互联网新热土视角下开源AI大模型与S2B2C商城小程序的县域市场渗透策略研究
  • 外文网站制作佛山做企业网站
  • 线上网站建设需求西安做网站 怎样备案