【原创·每日一题】John的篮球(DFS+回溯思想)

225 阅读2分钟

这是今天某同班同学问我的一道题目,主要涉及了DFS及回溯思想,和本人之前分享过的"八皇后问题"有异曲同工之妙。

题目描述

这里有个故事。

John是一名强壮的篮球运动员,在一次投篮练习中,用力过大将篮球扔到了篮球场隔壁的迷宫中。他着急于接下来的训练,因此直接冲进了迷宫,但是没想到篮球早已被工作人员放到了迷宫的终点。

接下来的事你应该猜到了,故事就变成了John走迷宫的故事。现在请你设计一个程序,帮John计算一下他至少要走几步才能到达迷宫的终点,拿到篮球。

迷宫用一个二维数组构建的字符矩阵来表示。字符S表示John所在的位置。字符E表示篮球所在的位置。字符#表示墙壁。字符.表示道路。John每步可以在道路上朝上下左右任意一个方向走一个单位,但不能走出迷宫边界,也不能撞到墙壁。

输入与输出要求

编写一个名为basketball的函数,调用它输入一个二维数组。二维数组第一层代表迷宫若干行,第二层含若干个字符,代表迷宫每行内的情况。字符含义如题目描述中所述,保证有且仅有一个SE

该函数返回John拿到篮球所需的最少步数。若John永远无法拿到篮球,则返回字符串no!

测试样例

InputOutput
[['.', 'S', '.', '.'],['#', '#', '#', '.'],['.', '.', 'E', '.']]5
[['.', 'S', '.', '.'],['.', 'E', '.', '.'],['.', '.', '.', '.']]1
[['.', 'S', '.', '.'],['#', '#', '#', '#'],['.', '.', 'E', '.']]no!

题解

关键代码的注释写得应该比较清楚了。

"use strict";
const directions = [{addX: 0, addY: -1}, {addX: 0, addY: 1}, {addX: -1, addY: 0}, {addX: 1, addY: 0}];
function createChecker(matrix, hasVisitedRecord) {
    return (x, y) => x >=0 && y >=0 && y < matrix.length && x < matrix[0].length && matrix[y][x] !== '#' && !hasVisitedRecord.has(`${x}_${y}`);
}
function dfs(matrix, {x, y}, step, stepRecord, hasVisitedRecord, checker) {
    for(let {addX, addY} of directions) {  //逐次尝试四个方向
        let newX = x + addX;
        let newY = y + addY;
        if(checker(newX, newY)) {  //检测当前尝试的方向是否可以走
           //如果找到终点,记录步数,不再继续往下搜索
           //注意不能用step++,因为其他几个方向可能都还没尝试,走到终点后仍需退回上一格去尝试其他方向
           if(matrix[newY][newX] === 'E') return stepRecord.add(step+1);
           //如果此路为活路,继续向下搜索
           hasVisitedRecord.add(`${newX}_${newY}`);  //记录当前坐标已经走过,防止走回头路
           dfs(matrix, {x: newX, y: newY}, ++step, stepRecord, hasVisitedRecord, checker);  //以当前格为起点继续向下搜索
           //执行到这里说明上一步所作搜索已经结束,需要进行回溯
           //然后尝试剩余的还未尝试的方向(如果还剩的话)
           step--;
           hasVisitedRecord.delete(`${newX}_${newY}`);
        }
    }
}
function basketball(matrix) {
    let startPoint;
    let hasVisitedRecord = new Set();
    let stepRecord = new Set();
    //查找起点坐标
    matrix.forEach((arr, y) => 
        arr.forEach((str, x) => 
            str === 'S' && (startPoint = {x, y}, hasVisitedRecord.add(`${x}_${y}`))
        )
    );
    //从起点出发开始DFS搜索
    dfs(matrix, startPoint, 0, stepRecord, hasVisitedRecord, createChecker(matrix, hasVisitedRecord));
    //输出结果
    return stepRecord.size ? Math.min(...stepRecord) : 'no!';
}

测试结果

参考资料

www.cnblogs.com/yspworld/p/…