登录
转载

leetcode-LCP 13 - 寻宝

发布于 2025-02-14 阅读 81
  • GitHub
  • LeetCode
转载

LCP 13 - 寻宝

二 题目

题目查看:https://leetcode-cn.com/problems/xun-bao/
/**
 * @param {string[]} maze
 * @return {number}
 */
var minimalSteps = function(maze) {

};

根据上面的已知函数,小伙伴们可以先尝试破解本题,确定了自己的答案后再看下面代码。

三 解题思路

/**
 * @param {string[]} maze
 * @return {number}
 * 求从起点到所有石头的最短步数,
 * 求各个机关到终点的最短步数
 * 求各个机关到各个石头的最短步数
 * 进而求出机关到机关的最短步数
 * 最后求出从起点出发到激活所有机关的最短步数
 * 最后求从起点出发激活所有机关 + 从最后一个机关到终点的最短步数
 * 一堆石头有无限个石头, 一个石头只能激活一个机关
 */
// 二位数组记录从某个点出发到其他点的最小步数
let dist = null
// 求dist的时候记录某个点是否够已经访问过
let visited = null
// 记录各个机关到各堆巨石的最小步数
let distM = null
// 记录从起点出发到各个石块的最小步数
let distS = null
// 机关经石头到各个机关的最小步数
let distBM = null
// 各个机关到终点的最小步数
let distT = null


// 记录各个机关到终点的最小步数
const dirs = [[-1, 0], [0, 1], [1, 0], [0, -1]]

// 所有机关
let puzzles = null
// 所有石堆位置
let stones = null

var minimalSteps = function(maze) {
    let stoneNum = 0
    let puzzleNum = 0
    let start = null
    let end = null
    const m = maze.length
    const n = maze[0].length
    stones = []
    puzzles = []
    distS = generateArray(m, n, -1)
    distT = generateArray(m, n, -1)
    for(let i = 0; i < m; i++) {
        for(let j = 0; j < n; j++) {
            // 石头
            if(maze[i][j] === 'O') {
                stones.push([i, j])
                stoneNum++
                // 机关
            } else if(maze[i][j] === 'M'){
                puzzles.push([i, j])
                puzzleNum++
                // 起点
            } else if(maze[i][j] === 'S') {
                start = [i, j]
                // 终点
            } else if(maze[i][j] === 'T') {
                end = [i, j]
            }
        }
    }
    distM= generateArray(puzzleNum, stoneNum, -1)
    distBM = generateArray(puzzleNum, puzzleNum, -1)
    bfs(maze, start, m, n)
    if(puzzleNum === 0) return dist[end[0]][end[1]]

    // 求起点到各个石堆的最小步数
    for(let stone of stones) distS[stone[0]][stone[1]] = dist[stone[0]][stone[1]]
    bfs(maze, end, m, n)
    // 求终点到各个机关的最小步数
    for(let p of puzzles) distT[p[0]][p[1]] = dist[p[0]][p[1]]
    // 求各个机关到各个石堆的最小步数
    for(let i = 0; i < puzzleNum; i++) {
        bfs(maze, puzzles[i], m, n)
        for(let j = 0; j < stoneNum; j++) {
            distM[i][j] = dist[stones[j][0]][stones[j][1]]
        }
    }
    /**
     * 接下来求从起点出发开启所有机关, 最后一个机关是某个的最小步数,分量不,
     * 第一步求得从起点出发只开启一个机关的最小步数
     * 第二步根据第一步的结果生成从起点出发开启所有机关、最后一个开启的机关是某一个的最小步数
     */

    let all = 1 << puzzleNum
    // dp 是二维数组, 第一维二进制表示某一位为1表示这一机关是开启状态, 第二位只是最后开启的是哪个机关
    let dp = generateArray(all, 16, -1)
    for(let i = 0; i < puzzleNum; i++) {
        let idx = 1 << i
        let best = -1
        for(let j = 0; j < stoneNum; j++) {
            if((distS[stones[j][0]][stones[j][1]] === -1) || (distM[i][j] === -1)) continue
            let value = distS[stones[j][0]][stones[j][1]] + distM[i][j]
            if((best === -1) || value < best) best = value
        }
        dp[idx][i] = best
    }

    // 求出机关到机关之间经过巨石的最短步数, 因为一块石头只能开启一个机关
    for(let i = 0; i < puzzleNum; i++) {
        for(let j = 0; j < puzzleNum; j++) {
            if(i !== j) {
                let best = -1
                for(let k = 0; k < stoneNum; k++) {
                    if((distM[i][k] !== -1) && (distM[j][k] !== -1)) {
                        let value = distM[i][k] + distM[j][k]
                        if((best === -1) || (value < best)) best = value
                    }
                }
                distBM[i][j] = best
            } else distBM[i][j] = 0
        }
    }

    // 求出从起点出发激活所有机关、最后一个机关被激活的最小步数, 求得是dp[i][j]
    for(let i = 1; i < all; i++) {
        for(let j = 0; j < puzzleNum; j++) {
            // 每一轮只求j
            if(i & (1 << j)) {
                for(let k = 0; k < puzzleNum; k++) {
                    // j != k && (i & (1 << k)) && dp[i - (1 << j)][k] != -1 && distBM[k][j] != -1
                    // j 不等于k, k已被开启、j未被开启的情况存在, k、j之间有路径可达
                    if((j !== k) && (i & (1 << k)) && (dp[i - (1 << j)][k] !== -1) && (distBM[k][j] !== -1)) {
                        let value = dp[i - (1 << j)][k] + distBM[k][j]
                         // 谁小我就从谁过来
                        if((dp[i][j] === -1) || (value < dp[i][j])) dp[i][j] = value
                    }
                }
            }
        }
    }

    /**
     * 最后求开启所有机关最小步数 + 从一个机关到终点的最小步数的和的最小值,这个就是答案
     *
     */
    let ans = Infinity
    let left = all - 1 // that is 1111111111111111
    for(let i = 0; i < puzzleNum; i++) {
        if((dp[left][i] !== -1) && (distT[puzzles[i][0]][puzzles[i][1]] !== -1)) {
            let value = dp[left][i] + distT[puzzles[i][0]][puzzles[i][1]]
            if(value < ans) ans = value
        }
    }

    return ans === Infinity ? -1 : ans
};


function bfs(maze, entry, m, n) {

    dist = generateArray(m, n, -1)
    visited = generateArray(m, n , 0)

    let queue = [entry]
    visited[entry[0]][entry[1]] = 1
    dist[entry[0]][entry[1]] = 0
    let step = 0
    while(queue.length) {
        step++
        let len = queue.length
        for (let i = 0; i < len; i++) {
            let [x, y] = queue.pop()
            for (let dir of dirs) {
                let newX = x + dir[0]
                let newY = y + dir[1]
                if ((newX < 0) || (newX >= m) || (newY < 0) || (newY >= n) || visited[newX][newY] || (maze[newX][newY] === '#')) {
                    continue
                }
                dist[newX][newY] = step
                visited[newX][newY] = 1
                queue.unshift([newX, newY])
            }
        }
    }
}

function generateArray(m, n, fill){
    let arr = new Array(m)
    for(let i = 0; i < m; i++) arr[i] = new Array(n).fill(fill)
    return arr
}

四 统计分析

本题不需要统计分析。

五 套路分析

本题暂未发现任何套路,如果有但是 jsliang 后面发现了的话,会在 GitHub 进行补充。

知识共享许可协议
jsliang 的文档库梁峻荣 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于https://github.com/LiangJunrong/document-library上的作品创作。
本许可协议授权之外的使用权限可以从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处获得。

评论区

leon
2粉丝

励志做一条安静的咸鱼,从此走上人生巅峰。

0

0

4

举报