499. The Maze III
leetcode hard
Task
В лабиринте есть мячик с пустыми пространствами (0) и стенами (1). Мячик может катиться вверх, вниз, влево или вправо, пока не столкнется со стеной, затем выбрать новое направление. В лабиринте также есть отверстие, куда мячик упадет, если закатится в него.
Дан лабиринт размером m x n, позиция мяча ball и отверстия hole, где ball = [ballrow, ballcol] и hole = [holerow, holecol]. Верните строку instructions с кратчайшим путем мячика к отверстию. Если существует несколько вариантов, верните лексикографически минимальный. Если путь невозможен, верните "impossible". Ответ должен содержать 'u' (вверх), 'd' (вниз), 'l' (влево) и 'r' (вправо).
Расстояние — это количество пройденных пустых пространств от начальной позиции (исключительно) до конечной (включительно).
Вы можете предположить, что границы лабиринта — это стены. В примере ниже они не указаны.
Пример:
Input: maze = [[0,0,0,0,0],[1,1,0,0,1],[0,0,0,0,0],[0,1,0,0,1],[0,1,0,0,0]], ball = [4,3], hole = [0,1]
Output: "lul"
C# solution
matched/originalusing System;
using System.Collections.Generic;
public class State : IComparable<State> {
public int Row, Col, Dist;
public string Path;
public State(int r, int c, int d, string p) { Row = r; Col = c; Dist = d; Path = p; }
public int CompareTo(State other) {
return Dist == other.Dist ? string.Compare(Path, other.Path, StringComparison.Ordinal) : Dist - other.Dist;
}
}
public class Solution {
private static readonly int[][] Directions = { new[] {0, -1}, new[] {-1, 0}, new[] {0, 1}, new[] {1, 0} };
private static readonly string[] TextDirections = { "l", "u", "r", "d" };
public string FindShortestWay(int[][] maze, int[] ball, int[] hole) {
int m = maze.Length, n = maze[0].Length;
var heap = new PriorityQueue<State>();
var seen = new bool[m, n];
heap.Enqueue(new State(ball[0], ball[1], 0, ""));
while (heap.Count > 0) {
var curr = heap.Dequeue();
if (seen[curr.Row, curr.Col]) continue;
if (curr.Row == hole[0] && curr.Col == hole[1]) return curr.Path;
seen[curr.Row, curr.Col] = true;
foreach (var (dy, dx, direction) in Directions.Zip(TextDirections)) {
int r = curr.Row, c = curr.Col, dist = 0;
while (Valid(r + dy, c + dx, maze, m, n)) {
r += dy; c += dx; dist++;
if (r == hole[0] && c == hole[1]) break;
}
heap.Enqueue(new State(r, c, curr.Dist + dist, curr.Path + direction));
}
}
return "impossible";
}
private bool Valid(int row, int col, int[][] maze, int m, int n) {
return row >= 0 && row < m && col >= 0 && col < n && maze[row][col] == 0;
}
}C++ solution
auto-draft, review before submit#include <bits/stdc++.h>
using namespace std;
// Auto-generated C++ draft from the C# solution. Review containers, LINQ and helper types before submit.
public class State : IComparable<State> {
public int Row, Col, Dist;
public string Path;
public State(int r, int c, int d, string p) { Row = r; Col = c; Dist = d; Path = p; }
public int CompareTo(State other) {
return Dist == other.Dist ? string.Compare(Path, other.Path, StringComparison.Ordinal) : Dist - other.Dist;
}
}
class Solution {
public:
private static readonly int[][] Directions = { new[] {0, -1}, new[] {-1, 0}, new[] {0, 1}, new[] {1, 0} };
private static readonly vector<string> TextDirections = { "l", "u", "r", "d" };
public string FindShortestWay(int[][] maze, vector<int>& ball, vector<int>& hole) {
int m = maze.size(), n = maze[0].size();
var heap = new PriorityQueue<State>();
var seen = new bool[m, n];
heap.Enqueue(new State(ball[0], ball[1], 0, ""));
while (heap.size() > 0) {
var curr = heap.Dequeue();
if (seen[curr.Row, curr.Col]) continue;
if (curr.Row == hole[0] && curr.Col == hole[1]) return curr.Path;
seen[curr.Row, curr.Col] = true;
foreach (var (dy, dx, direction) in Directions.Zip(TextDirections)) {
int r = curr.Row, c = curr.Col, dist = 0;
while (Valid(r + dy, c + dx, maze, m, n)) {
r += dy; c += dx; dist++;
if (r == hole[0] && c == hole[1]) break;
}
heap.Enqueue(new State(r, c, curr.Dist + dist, curr.Path + direction));
}
}
return "impossible";
}
private bool Valid(int row, int col, int[][] maze, int m, int n) {
return row >= 0 && row < m && col >= 0 && col < n && maze[row][col] == 0;
}
}Java solution
matched/originalclass State {
int row, col, dist;
String path;
State(int r, int c, int d, String p) { row = r; col = c; dist = d; path = p; }
}
class Solution {
int[][] directions = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}};
String[] textDirections = {"l", "u", "r", "d"};
int m, n;
public String findShortestWay(int[][] maze, int[] ball, int[] hole) {
m = maze.length; n = maze[0].length;
PriorityQueue<State> heap = new PriorityQueue<>((a, b) -> a.dist == b.dist ? a.path.compareTo(b.path) : a.dist - b.dist);
boolean[][] seen = new boolean[m][n];
heap.add(new State(ball[0], ball[1], 0, ""));
while (!heap.isEmpty()) {
State curr = heap.remove();
if (seen[curr.row][curr.col]) continue;
if (curr.row == hole[0] && curr.col == hole[1]) return curr.path;
seen[curr.row][curr.col] = true;
for (State nextState : getNeighbors(curr.row, curr.col, maze, hole)) {
heap.add(new State(nextState.row, nextState.col, curr.dist + nextState.dist, curr.path + nextState.path));
}
}
return "impossible";
}
private List<State> getNeighbors(int row, int col, int[][] maze, int[] hole) {
List<State> neighbors = new ArrayList<>();
for (int i = 0; i < 4; i++) {
int dy = directions[i][0], dx = directions[i][1], dist = 0, currRow = row, currCol = col;
String direction = textDirections[i];
while (valid(currRow + dy, currCol + dx, maze)) {
currRow += dy; currCol += dx; dist++;
if (currRow == hole[0] && currCol == hole[1]) break;
}
neighbors.add(new State(currRow, currCol, dist, direction));
}
return neighbors;
}
private boolean valid(int row, int col, int[][] maze) {
return row >= 0 && row < m && col >= 0 && col < n && maze[row][col] == 0;
}
}Python solution
matched/originalimport heapq
class State:
def __init__(self, r, c, d, p):
self.row = r
self.col = c
self.dist = d
self.path = p
def __lt__(self, other):
return self.dist == other.dist and self.path < other.path or self.dist < other.dist
class Solution:
def findShortestWay(self, maze, ball, hole):
m, n = len(maze), len(maze[0])
directions = [(0, -1), (-1, 0), (0, 1), (1, 0)]
textDirections = ["l", "u", "r", "d"]
heap = []
heapq.heappush(heap, State(ball[0], ball[1], 0, ""))
seen = [[False] * n for _ in range(m)]
while heap:
curr = heapq.heappop(heap)
if seen[curr.row][curr.col]:
continue
if [curr.row, curr.col] == hole:
return curr.path
seen[curr.row][curr.col] = True
for nextState in self.getNeighbors(curr.row, curr.col, maze, hole, directions, textDirections):
heapq.heappush(heap, State(nextState.row, nextState.col, curr.dist + nextState.dist, curr.path + nextState.path))
return "impossible"
def getNeighbors(self, row, col, maze, hole, directions, textDirections):
m, n = len(maze), len(maze[0])
neighbors = []
for i in range(4):
dy, dx = directions[i]
direction = textDirections[i]
currRow, currCol, dist = row, col, 0
while 0 <= currRow + dy < m and 0 <= currCol + dx < n and maze[currRow + dy][currCol + dx] == 0:
currRow += dy
currCol += dx
dist += 1
if [currRow, currCol] == hole:
break
neighbors.append(State(currRow, currCol, dist, direction))
return neighborsGo solution
matched/originalpackage main
import (
"container/heap"
)
type State struct {
row, col, dist int
path string
}
type PriorityQueue []*State
func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool { return pq[i].dist < pq[j].dist || (pq[i].dist == pq[j].dist && pq[i].path < pq[j].path) }
func (pq PriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] }
func (pq *PriorityQueue) Push(x interface{}) { *pq = append(*pq, x.(*State)) }
func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
*pq = old[:n-1]
return item
}
var directions = [4][2]int{{0, -1}, {-1, 0}, {0, 1}, {1, 0}}
var textDirections = []string{"l", "u", "r", "d"}
func findShortestWay(maze [][]int, ball, hole []int) string {
m, n := len(maze), len(maze[0])
pq := &PriorityQueue{}
heap.Init(pq)
heap.Push(pq, &State{ball[0], ball[1], 0, ""})
seen := make([][]bool, m)
for i := range seen {
seen[i] = make([]bool, n)
}
for pq.Len() > 0 {
curr := heap.Pop(pq).(*State)
if seen[curr.row][curr.col] {
continue
}
if curr.row == hole[0] && curr.col == hole[1] {
return curr.path
}
seen[curr.row][curr.col] = true
for i := 0; i < 4; i++ {
dy, dx := directions[i][0], directions[i][1]
currRow, currCol, dist := curr.row, curr.col, 0
for currRow+dy >= 0 && currRow+dy < m && currCol+dx >= 0 && currCol+dx < n && maze[currRow+dy][currCol+dx] == 0 {
currRow += dy
currCol += dx
dist++
if currRow == hole[0] && currCol == hole[1] {
break
}
}
heap.Push(pq, &State{currRow, currCol, curr.dist + dist, curr.path + textDirections[i]})
}
}
return "impossible"
}Explanation
Algorithm
Инициализация и вспомогательные функции
Создайте функцию valid для проверки, находится ли координата (row, col) в пределах лабиринта и является ли она пустым пространством. Создайте функцию getNeighbors для получения списка соседей для данной позиции. Двигайтесь в каждом направлении (вверх, вниз, влево, вправо) до встречи со стеной.
Запуск алгоритма Дейкстры
Инициализируйте очередь с начальной позицией мяча, где элементы с меньшим расстоянием имеют высокий приоритет, а при равных расстояниях выбирайте минимальную строку пути. Создайте структуру seen для отслеживания посещенных узлов.
Поиск кратчайшего пути
Пока очередь не пуста, извлекайте узел с наименьшим расстоянием. Если узел посещен, пропустите его. Если это отверстие, верните текущий путь. Отметьте узел как посещенный, добавьте его соседей в очередь, обновив расстояние и путь. Если пути нет, верните "impossible".
😎