mirror of
https://github.com/halfrost/LeetCode-Go.git
synced 2025-07-24 02:14:00 +08:00
187 lines
4.5 KiB
Go
187 lines
4.5 KiB
Go
package leetcode
|
||
|
||
import (
|
||
"math"
|
||
"strings"
|
||
)
|
||
|
||
var dir = [][]int{
|
||
{-1, 0},
|
||
{0, 1},
|
||
{1, 0},
|
||
{0, -1},
|
||
}
|
||
|
||
// 解法一 BFS,利用状态压缩来过滤筛选状态
|
||
func shortestPathAllKeys(grid []string) int {
|
||
if len(grid) == 0 {
|
||
return 0
|
||
}
|
||
board, visited, startx, starty, res, fullKeys := make([][]byte, len(grid)), make([][][]bool, len(grid)), 0, 0, 0, 0
|
||
for i := 0; i < len(grid); i++ {
|
||
board[i] = make([]byte, len(grid[0]))
|
||
}
|
||
for i, g := range grid {
|
||
board[i] = []byte(g)
|
||
for _, v := range g {
|
||
if v == 'a' || v == 'b' || v == 'c' || v == 'd' || v == 'e' || v == 'f' {
|
||
fullKeys |= (1 << uint(v-'a'))
|
||
}
|
||
}
|
||
if strings.Contains(g, "@") {
|
||
startx, starty = i, strings.Index(g, "@")
|
||
}
|
||
}
|
||
for i := 0; i < len(visited); i++ {
|
||
visited[i] = make([][]bool, len(board[0]))
|
||
}
|
||
for i := 0; i < len(board); i++ {
|
||
for j := 0; j < len(board[0]); j++ {
|
||
visited[i][j] = make([]bool, 64)
|
||
}
|
||
}
|
||
queue := []int{}
|
||
queue = append(queue, (starty<<16)|(startx<<8))
|
||
visited[startx][starty][0] = true
|
||
for len(queue) != 0 {
|
||
qLen := len(queue)
|
||
for i := 0; i < qLen; i++ {
|
||
state := queue[0]
|
||
queue = queue[1:]
|
||
starty, startx = state>>16, (state>>8)&0xFF
|
||
keys := state & 0xFF
|
||
if keys == fullKeys {
|
||
return res
|
||
}
|
||
for i := 0; i < 4; i++ {
|
||
newState := keys
|
||
nx := startx + dir[i][0]
|
||
ny := starty + dir[i][1]
|
||
if !isInBoard(board, nx, ny) {
|
||
continue
|
||
}
|
||
if board[nx][ny] == '#' {
|
||
continue
|
||
}
|
||
flag, canThroughLock := keys&(1<<(board[nx][ny]-'A')), false
|
||
if flag != 0 {
|
||
canThroughLock = true
|
||
}
|
||
if isLock(board, nx, ny) && !canThroughLock {
|
||
continue
|
||
}
|
||
if isKey(board, nx, ny) {
|
||
newState |= (1 << (board[nx][ny] - 'a'))
|
||
}
|
||
if visited[nx][ny][newState] {
|
||
continue
|
||
}
|
||
queue = append(queue, (ny<<16)|(nx<<8)|newState)
|
||
visited[nx][ny][newState] = true
|
||
}
|
||
}
|
||
res++
|
||
}
|
||
return -1
|
||
}
|
||
|
||
func isInBoard(board [][]byte, x, y int) bool {
|
||
return x >= 0 && x < len(board) && y >= 0 && y < len(board[0])
|
||
}
|
||
|
||
// 解法二 DFS,但是超时了,剪枝条件不够强
|
||
func shortestPathAllKeys1(grid []string) int {
|
||
if len(grid) == 0 {
|
||
return 0
|
||
}
|
||
board, visited, startx, starty, res, fullKeys := make([][]byte, len(grid)), make([][][]bool, len(grid)), 0, 0, math.MaxInt64, 0
|
||
for i := 0; i < len(grid); i++ {
|
||
board[i] = make([]byte, len(grid[0]))
|
||
}
|
||
for i, g := range grid {
|
||
board[i] = []byte(g)
|
||
for _, v := range g {
|
||
if v == 'a' || v == 'b' || v == 'c' || v == 'd' || v == 'e' || v == 'f' {
|
||
fullKeys |= (1 << uint(v-'a'))
|
||
}
|
||
}
|
||
if strings.Contains(g, "@") {
|
||
startx, starty = i, strings.Index(g, "@")
|
||
}
|
||
}
|
||
for i := 0; i < len(visited); i++ {
|
||
visited[i] = make([][]bool, len(board[0]))
|
||
}
|
||
for i := 0; i < len(board); i++ {
|
||
for j := 0; j < len(board[0]); j++ {
|
||
visited[i][j] = make([]bool, 64)
|
||
}
|
||
}
|
||
searchKeys(board, &visited, fullKeys, 0, (starty<<16)|(startx<<8), &res, []int{})
|
||
if res == math.MaxInt64 {
|
||
return -1
|
||
}
|
||
return res - 1
|
||
}
|
||
|
||
func searchKeys(board [][]byte, visited *[][][]bool, fullKeys, step, state int, res *int, path []int) {
|
||
y, x := state>>16, (state>>8)&0xFF
|
||
keys := state & 0xFF
|
||
|
||
if keys == fullKeys {
|
||
*res = min(*res, step)
|
||
return
|
||
}
|
||
|
||
flag, canThroughLock := keys&(1<<(board[x][y]-'A')), false
|
||
if flag != 0 {
|
||
canThroughLock = true
|
||
}
|
||
newState := keys
|
||
//fmt.Printf("x = %v y = %v fullKeys = %v keys = %v step = %v res = %v path = %v state = %v\n", x, y, fullKeys, keys, step, *res, path, state)
|
||
if (board[x][y] != '#' && !isLock(board, x, y)) || (isLock(board, x, y) && canThroughLock) {
|
||
if isKey(board, x, y) {
|
||
newState |= (1 << uint(board[x][y]-'a'))
|
||
}
|
||
(*visited)[x][y][newState] = true
|
||
path = append(path, x)
|
||
path = append(path, y)
|
||
|
||
for i := 0; i < 4; i++ {
|
||
nx := x + dir[i][0]
|
||
ny := y + dir[i][1]
|
||
if isInBoard(board, nx, ny) && !(*visited)[nx][ny][newState] {
|
||
searchKeys(board, visited, fullKeys, step+1, (ny<<16)|(nx<<8)|newState, res, path)
|
||
}
|
||
}
|
||
(*visited)[x][y][keys] = false
|
||
path = path[:len(path)-1]
|
||
path = path[:len(path)-1]
|
||
}
|
||
}
|
||
|
||
func isLock(board [][]byte, x, y int) bool {
|
||
if (board[x][y] == 'A') || (board[x][y] == 'B') ||
|
||
(board[x][y] == 'C') || (board[x][y] == 'D') ||
|
||
(board[x][y] == 'E') || (board[x][y] == 'F') {
|
||
return true
|
||
}
|
||
return false
|
||
}
|
||
|
||
func isKey(board [][]byte, x, y int) bool {
|
||
if (board[x][y] == 'a') || (board[x][y] == 'b') ||
|
||
(board[x][y] == 'c') || (board[x][y] == 'd') ||
|
||
(board[x][y] == 'e') || (board[x][y] == 'f') {
|
||
return true
|
||
}
|
||
return false
|
||
}
|
||
|
||
func min(a int, b int) int {
|
||
if a > b {
|
||
return b
|
||
}
|
||
return a
|
||
}
|