Hard
题目描述
给你一个 m x n 的网格图 grid 。网格中每个格子都有一个方向标志,指向你应该访问的下一个格子。格子 grid[i][j] 中的标志可能为:
1:下一个格子是右边的格子,即你会从grid[i][j]走到grid[i][j + 1]2:下一个格子是左边的格子,即你会从grid[i][j]走到grid[i][j - 1]3:下一个格子是下边的格子,即你会从grid[i][j]走到grid[i + 1][j]4:下一个格子是上边的格子,即你会从grid[i][j]走到grid[i - 1][j]
注意网格中可能会有指向网格外面的标志。
你将从左上角的格子 (0, 0) 开始出发。网格中的「有效路径」是指从左上角格子 (0, 0) 开始、一直到右下角 (m - 1, n - 1) 结束,且路径中每一步都按照路标的方向前进的路径。有效路径不需要是最短路径。
你可以花费 cost = 1 的代价修改一个格子中的标志(修改一次)。
请你返回让网格中至少有一条有效路径的最小代价。
示例 1:
输入:grid = [[1,1,1,1],[2,2,2,2],[1,1,1,1],[2,2,2,2]]
输出:3
解释:你将从点 (0, 0) 出发。
到达 (3, 3) 的路径为: (0, 0) --> (0, 1) --> (0, 2) --> (0, 3) 花费代价 1 使方向向下 --> (1, 3) --> (1, 2) --> (1, 1) --> (1, 0) 花费代价 1 使方向向下 --> (2, 0) --> (2, 1) --> (2, 2) --> (2, 3) 花费代价 1 使方向向下 --> (3, 3)
总代价 = 3.
示例 2:
输入:grid = [[1,1,3],[3,2,2],[1,1,4]]
输出:0
解释:不修改标志你就可以从 (0, 0) 到达 (2, 2) 。
示例 3:
输入:grid = [[1,2],[4,3]]
输出:1
提示:
m == grid.lengthn == grid[i].length1 <= m, n <= 1001 <= grid[i][j] <= 4
解题思路
这是一个典型的最短路径问题,需要用到0-1 BFS算法。
核心思路:
- 将网格看作图,每个格子可以向四个方向移动
- 如果当前格子的方向标志指向目标格子,则移动代价为0;否则需要修改标志,代价为1
- 使用双端队列进行BFS:代价为0的路径加入队头,代价为1的路径加入队尾
- 这样保证了队列中的元素按代价递增排列,第一次到达终点的路径就是最小代价
算法步骤:
- 初始化方向数组,对应四个方向的移动
- 使用双端队列,从起点(0,0)开始,代价为0
- 对于当前位置,检查四个方向:
- 如果是当前格子标志指向的方向,代价为0,加入队头
- 否则代价为1,加入队尾
- 当第一次到达终点时,返回当前代价
推荐解法:0-1 BFS 时间复杂度O(mn),空间复杂度O(mn),是此类问题的最优解法。
代码实现
class Solution {
public:
int minCost(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
vector<vector<int>> dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
vector<vector<int>> cost(m, vector<int>(n, INT_MAX));
deque<pair<int, int>> dq;
dq.push_back({0, 0});
cost[0][0] = 0;
while (!dq.empty()) {
auto [x, y] = dq.front();
dq.pop_front();
if (x == m - 1 && y == n - 1) {
return cost[x][y];
}
for (int d = 0; d < 4; d++) {
int nx = x + dirs[d][0];
int ny = y + dirs[d][1];
if (nx >= 0 && nx < m && ny >= 0 && ny < n) {
int newCost = cost[x][y] + (grid[x][y] != d + 1);
if (newCost < cost[nx][ny]) {
cost[nx][ny] = newCost;
if (grid[x][y] == d + 1) {
dq.push_front({nx, ny});
} else {
dq.push_back({nx, ny});
}
}
}
}
}
return cost[m - 1][n - 1];
}
};
class Solution:
def minCost(self, grid: List[List[int]]) -> int:
from collections import deque
m, n = len(grid), len(grid[0])
dirs = [(0, 1), (0, -1), (1, 0), (-1, 0)]
cost = [[float('inf')] * n for _ in range(m)]
dq = deque()
dq.append((0, 0))
cost[0][0] = 0
while dq:
x, y = dq.popleft()
if x == m - 1 and y == n - 1:
return cost[x][y]
for d in range(4):
nx, ny = x + dirs[d][0], y + dirs[d][1]
if 0 <= nx < m and 0 <= ny < n:
new_cost = cost[x][y] + (0 if grid[x][y] == d + 1 else 1)
if new_cost < cost[nx][ny]:
cost[nx][ny] = new_cost
if grid[x][y] == d + 1:
dq.appendleft((nx, ny))
else:
dq.append((nx, ny))
return cost[m - 1][n - 1]
public class Solution {
public int MinCost(int[][] grid) {
int m = grid.Length, n = grid[0].Length;
int[,] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int[,] cost = new int[m, n];
var dq = new LinkedList<(int, int)>();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
cost[i, j] = int.MaxValue;
}
}
dq.AddLast((0, 0));
cost[0, 0] = 0;
while (dq.Count > 0) {
var (x, y) = dq.First.Value;
dq.RemoveFirst();
if (x == m - 1 && y == n - 1) {
return cost[x, y];
}
for (int d = 0; d < 4; d++) {
int nx = x + dirs[d, 0];
int ny = y + dirs[d, 1];
if (nx >= 0 && nx < m && ny >= 0 && ny < n) {
int newCost = cost[x, y] + (grid[x][y] == d + 1 ? 0 : 1);
if (newCost < cost[nx, ny]) {
cost[nx, ny] = newCost;
if (grid[x][y] == d + 1) {
dq.AddFirst((nx, ny));
} else {
dq.AddLast((nx, ny));
}
}
}
}
}
return cost[m - 1, n - 1];
}
}
/**
* @param {number[][]} grid
* @return {number}
*/
var minCost = function(grid) {
const m = grid.length;
const n = grid[0].length;
const directions = [[0, 1], [0, -1], [1, 0], [-1, 0]];
const deque = [[0, 0, 0]]; // [row, col, cost]
const visited = new Set();
while (deque.length > 0) {
const [row, col, cost] = deque.shift();
if (row === m - 1 && col === n - 1) {
return cost;
}
const key = `${row},${col}`;
if (visited.has(key)) continue;
visited.add(key);
for (let dir = 0; dir < 4; dir++) {
const [dx, dy] = directions[dir];
const newRow = row + dx;
const newCol = col + dy;
if (newRow >= 0 && newRow < m && newCol >= 0 && newCol < n) {
const newKey = `${newRow},${newCol}`;
if (!visited.has(newKey)) {
const needsChange = grid[row][col] !== dir + 1;
const newCost = cost + (needsChange ? 1 : 0);
if (needsChange) {
deque.push([newRow, newCol, newCost]);
} else {
deque.unshift([newRow, newCol, newCost]);
}
}
}
}
}
return -1;
};
复杂度分析
| 复杂度 | 0-1 BFS |
|---|---|
| 时间复杂度 | O(mn) |
| 空间复杂度 | O(mn) |
说明:
- 时间复杂度:每个格子最多被访问常数次,总共mn个格子
- 空间复杂度:需要存储每个格子的最小代价,以及双端队列的空间