题目
小S在一个n×mn×m的网格迷宫中,初始位置在左上角 (1,1)(1,1),目标是到达右下角 (n,m)(n,m)。每个格子可以是黑色(表示为1)或者白色(表示为0)。他希望在移动过程中经过的黑色格子尽可能少。移动时可以向上、下、左、右四个方向移动,但不能走出迷宫的边界。请你帮小S计算从起点到终点所需经过的最少黑色格子的数量。
解题思路
这个题目是一个经典的 最短路径问题,但与普通的最短路径不同,它要求的是经过的黑色格子最少,而不仅仅是最短路径的步数。因此,我们可以通过 加权图的最短路径算法 来解决这个问题。
问题建模:
可以将迷宫看作一个 网格图,其中每个格子代表一个图的节点。
网格中的每个格子都有一个权值:如果是白色格子(0),权值是 0;如果是黑色格子(1),权值是 1。
起点是左上角 (1, 1),终点是右下角 (n, m),我们需要找到一条从起点到终点的路径,经过的黑色格子数最少。
使用广度优先搜索(BFS)或 Dijkstra 算法:由于每个格子的权值只有 0 或 1(这是一种特殊的加权图),我们可以利用 0-1 BFS 或 Dijkstra 算法 来解决这个问题。 0-1 BFS:0-1 BFS 是一种特殊的 BFS,适用于边权只有 0 和 1 的图。在该算法中,我们使用双端队列(deque)来优化队列的操作,使得当访问一个邻接点的边权为 0 时,可以将其放到队列前端,而当边权为 1 时,则放到队列后端。这就保证了从起点到终点的路径经过黑色格子的数量最少。 Dijkstra 算法:Dijkstra 算法适用于有非负权值的图,在每次选择最小的路径时,可以将黑色格子的数量作为权值,通过最小化路径权值来找到最优路径。
豆包MarsCode AI给出的算法步骤
- 初始化一个二维数组
dist,其中dist[i][j]表示到达位置(i, j)时经过的黑色格子数最少的值,初始值设为无穷大,起点(1, 1)设为 0。 - 使用 0-1 BFS 或 Dijkstra 算法进行遍历,更新每个位置的最少黑色格子数。
- 对于每个位置
(x, y),检查四个方向(上、下、左、右)是否可以走,如果能走并且经过的新位置(nx, ny)的黑色格子数比当前的最少值更小,则更新dist[nx][ny]。 - 最终,
dist[n][m]即为从起点(1, 1)到终点(n, m)的最少黑色格子数。
测试样例:
样例1:
输入:
n = 5 ,m = 3 ,grid = [[0, 1, 0], [0, 1, 1], [0, 1, 0], [1, 0, 0], [1, 0, 0]]
输出:1
样例2:
输入:
n = 4 ,m = 4 ,grid = [[0, 0, 1, 0], [1, 0, 1, 0], [1, 0, 0, 0], [1, 1, 1, 0]]
输出:0
样例3:
输入:
n = 3 ,m = 3 ,grid = [[0, 0, 0], [1, 1, 0], [1, 1, 0]]
输出:0
代码实现:
from collections import deque
def solution(n: int, m: int, grid: list) -> int:
# 移动的方向
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
# 初始化 BFS 队列
queue = deque([(0, 0)]) # 起点 (0, 0)
visited = [[float('inf')] * m for _ in range(n)]
visited[0][0] = grid[0][0] # 初始黑色格子数量
while queue:
x, y = queue.popleft()
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < n and 0 <= ny < m: # 确保在边界内
# 计算到邻居的黑色格子数量
new_black_count = visited[x][y] + grid[nx][ny]
# 如果新的黑色格子数量更少,则更新并加入队列
if new_black_count < visited[nx][ny]:
visited[nx][ny] = new_black_count
queue.append((nx, ny))
# 返回右下角的最少黑色格子数量
return visited[n-1][m-1]
if __name__ == '__main__':
print(solution(5, 3, [[0, 1, 0], [0, 1, 1], [0, 1, 0], [1, 0, 0], [1, 0, 0]]) == 1)
print(solution(4, 4, [[0, 0, 1, 0], [1, 0, 1, 0], [1, 0, 0, 0], [1, 1, 1, 0]]) == 0)
print(solution(3, 3, [[0, 0, 0], [1, 1, 0], [1, 1, 0]]) == 0)
时间复杂度:
- 空间复杂度:O(n * m),用于存储
dist数组和队列。 - 时间复杂度:每个节点最多被访问一次,每个操作的复杂度为 O(1),因此整体时间复杂度是 O(n * m)。
总结:
这个问题通过将其转化为 最短路径问题,并利用 0-1 BFS 或 Dijkstra 算法 来求解,可以有效地找出从起点到终点经过的最少黑色格子数。这种方法特别适合于边权为 0 或 1 的加权图问题。