2617. 网格图中最少访问的格子数

博客围绕在整数矩阵中从左上角移动到右下角所需最少格子数的问题展开。介绍了移动规则,给出示例。题解先尝试递归和记忆化搜索,均超时,后改用动态规划仍超时,最终提出利用线段树存储区间值以解决时间复杂度问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给你一个下标从 0 开始的 m x n 整数矩阵 grid 。你一开始的位置在 左上角 格子 (0, 0) 。

当你在格子 (i, j) 的时候,你可以移动到以下格子之一:

  • 满足 j < k <= grid[i][j] + j 的格子 (i, k) (向右移动),或者
  • 满足 i < k <= grid[i][j] + i 的格子 (k, j) (向下移动)。

请你返回到达 右下角 格子 (m - 1, n - 1) 需要经过的最少移动格子数,如果无法到达右下角格子,请你返回 -1 。

示例 1:

输入:grid = [[3,4,2,1],[4,2,3,1],[2,1,0,0],[2,4,0,0]]
输出:4
解释:上图展示了到达右下角格子经过的 4 个格子。

示例 2:

输入:grid = [[3,4,2,1],[4,2,1,1],[2,1,1,0],[3,4,1,0]]
输出:3
解释:上图展示了到达右下角格子经过的 3 个格子。

示例 3:

输入:grid = [[2,1,0],[1,0,0]]
输出:-1
解释:无法到达右下角格子。

题解:这道题递归很好写,直接枚举就行。

class Solution {
    public static int minimumVisitedCells(int[][] grid) {
		int m = grid.length;
		int n = grid[0].length;
		int tmp = process(grid, 0, 0, m, n);
		return tmp==0||tmp>=m+n?-1:tmp;
	}

	public static int process(int[][] grid, int i, int j, int m, int n) {
		//System.out.println(i+","+j);
		if (i >= m || j >= n) {
			return m+n;
		}
		if (i == m - 1 && j == n - 1) {
			return 1;
		}

		// 分为两种情况,向右走
		int current = grid[i][j];
		int min1 = m+n;
		for (int jx = j + 1; jx <= current + j && jx < n; jx++) {
			min1 = Math.min(process(grid, i, jx, m, n) + 1, min1);
		}
		//System.out.println("min1:"+min1);
			
		// 分为两种情况,向下走
		int min2 = m+n;
		for (int ix = i + 1; ix <= current + i && ix < m; ix++) {
			min2 = Math.min(process(grid, ix, j, m, n) + 1, min2);
		}
		//System.out.println("min2:"+min2);
		return Math.min(min1, min2);
	}
}

然后用记忆化搜索,就是用一个数组将不必要的递归不需要进行。

class Solution {
    public static int minimumVisitedCells(int[][] grid) {
		int m = grid.length;
		int n = grid[0].length;
		int[][] dp = new int[m+1][n+1];
		int tmp = process(grid, 0, 0, m, n,dp);
		return tmp == 0 || tmp >= m + n ? -1 : tmp;
	}

	public static int process(int[][] grid, int i, int j, int m, int n,int[][] dp) {
		if(dp[i][j]!=0)
			return dp[i][j];
		if (i >= m || j >= n) {
			dp[i][j] = m+n;
			return dp[i][j];
		}
		if (i == m - 1 && j == n - 1) {
			return 1;
		}

		// 分为两种情况,向右走
		int current = grid[i][j];
		int min1 = m + n;
		for (int jx = j+1; jx <= current + j && jx < n; jx++) {
			min1 = Math.min(process(grid, i, jx, m, n,dp) + 1, min1);
		}


		// 分为两种情况,向下走
		int min2 = m + n;
		for (int ix = i + 1; ix <= current + i && ix < m; ix++) {
			min2 = Math.min(process(grid, ix, j, m, n,dp) + 1, min2);
		}
		dp[i][j] = Math.min(min1, min2);
		return dp[i][j];
	}
}

结果还是超时,然后将记忆化搜索改为动态规划。

class Solution {
    public static int minimumVisitedCells(int[][] grid) {
		int m = grid.length;
		int n = grid[0].length;
		int[][] dp = new int[m + 1][n + 1];
		for(int i=0;i<m;i++) {
			for(int j = 0;j<n;j++) {
				dp[i][j] = m+n;
			}
		}
		
		dp[m - 1][n - 1] = 1;

		for (int i = m-1; i >= 0; i--) {
			for (int j = n-1; j >= 0; j--) {
				// 分为两种情况,向右走
				int current = grid[i][j];
				int min1 = m + n;
				for (int jx = j + 1; jx <= current + j && jx < n; jx++) {
					min1 = Math.min(dp[i][jx] + 1, min1);
				}

				// 分为两种情况,向下走
				int min2 = m + n;
				for (int ix = i + 1; ix <= current + i && ix < m; ix++) {
					min2 = Math.min(dp[ix][j] + 1, min2);
				}
				dp[i][j] = Math.min(Math.min(min1, min2),dp[i][j]);
			}
		}
		return dp[0][0] == 0 || dp[0][0] >= m + n ? -1 : dp[0][0];
	}
}

结果还是超时,在这里,我们发现每一次枚举最小值是不必要的,要是在求出dp的同时能存储在某个区间的值,时间复杂度是O(1),就可以解决问题了。可以利用线段树来实现。

### 解决方案概述 在 N×M 的网格中寻找从起点到终点的最短路径问题可以采用广度优先搜索(BFS)算法来解决。此方法适用于无权图中的最短路径计算,能够有效处理避开障碍物的情况。 以下是基于 BFS 实现的一个通用 C++ 程序框架: ```cpp #include <iostream> #include <queue> #include <vector> using namespace std; struct Node { int x, y; int steps; }; bool isValid(int newX, int newY, const vector<vector<int>>& grid, vector<vector<bool>>& visited, int rows, int cols) { return newX >= 0 && newX < rows && newY >= 0 && newY < cols && !visited[newX][newY] && grid[newX][newY] != 1; // 障碍物标记为1[^3] } int shortestPath(vector<vector<int>> grid, pair<int, int> start, pair<int, int> end) { int rows = grid.size(); if (rows == 0) return -1; int cols = grid[0].size(); vector<vector<bool>> visited(rows, vector<bool>(cols, false)); queue<Node> q; q.push(Node{start.first, start.second, 0}); visited[start.first][start.second] = true; vector<pair<int, int>> directions = {{0, 1}, {1, 0}}; // 只能向右或向下移动[^4] while (!q.empty()) { Node current = q.front(); q.pop(); if (current.x == end.first && current.y == end.second) { return current.steps; } for (auto& dir : directions) { int newX = current.x + dir.first; int newY = current.y + dir.second; if (isValid(newX, newY, grid, visited, rows, cols)) { visited[newX][newY] = true; q.push(Node{newX, newY, current.steps + 1}); } } } return -1; // 如果无法到达目的地,则返回-1表示不可达 } ``` 上述代码实现了基本的 BFS 方法用于求解二维网格中最短路径问题。其中 `grid` 表示输入的地图矩阵,值为 1 的单元格代表障碍物;`start` 和 `end` 是起始点与目标点坐标对。 #### 关键点解释 - **结构体定义**:通过创建一个名为 `Node` 的结构体存储当前节点的位置 `(x,y)` 和已走过的步 `steps`。 - **有效性检查函 (`isValid`):验证新位置是否越界、未访问过以及不是障碍物[^5]**。 - **队列操作**:利用标准库中的 `std::queue` 来管理待探索的节点列表。 - **方向组**:仅允许向右和向下两个方向移动,这符合题目约束条件[^6]。 ### 示例运行 假设我们有如下地图据: ```cpp vector<vector<int>> grid = { {0, 0, 0}, {0, 1, 0}, {0, 0, 0} }; pair<int, int> start(0, 0); pair<int, int> end(2, 2); cout << "Minimum Steps Required: " << shortestPath(grid, start, end); // 输出应为4 ``` 该例子展示了如何调用上面编写的函并打印结果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值