【大厂机试题+多种解法+算法可视化笔记】欢乐的周末

题目

小华和小为是很要好的朋友,他们约定周末一起吃饭。
通过手机交流,他们在地图上选择了多个聚餐地点(由于自然地形等原因,部分聚餐地点不可达),求小华和小为都能到达的聚餐地点有多少个?

输入描述
第一行输入m和n,m代表地图的长度,n代表地图的宽度。
第二行开始具体输入地图信息,地图信息包含:
0 为通畅的道路
1 为障碍物(且仅1为障碍物)
2 为小华或者小为,地图中必定有且仅有2个 (非障碍物)
3 为被选中的聚餐地点(非障碍物)

输出描述
可以被两方都到达的聚餐地点数量,行末无空格。

用例

输入示例输出说明
4 4
2 1 0 3
0 1 2 1
0 3 0 0
0 0 0 0
2第一行输入地图的长宽为3和4。第二行开始为具体的地图,其中:3代表小华和小明选择的聚餐地点;2代表小华或者小明(确保有2个);0代表可以通行的位置;1代表不可以通行的位置。此时两者能都能到达的聚餐位置有2处。
4 4
2 1 2 3
0 1 0 0
0 1 0 0
0 1 0 0
0第一行输入地图的长宽为4和4。第二行开始为具体的地图,其中:3代表小华和小明选择的聚餐地点;2代表小华或者小明(确保有2个);0代表可以通行的位置;1代表不可以通行的位置。由于图中小华和小为之间有个阻隔,此时,没有两人都能到达的聚餐地址,故而返回0。
备注地图的长宽为m和n,其中:4 <= m <= 100  4 <= n <= 100

思路

     根据题目描述可以想到“地图”“通过矩阵行列映射存储图的顶点和边信息”“图算法”“路径搜索”“递归”“深度优先/广度优先搜索”。因此此题可以通过图算法的深度优先搜索(DFS)广度优先搜索(BFS)两种方法统计可以到达的聚餐地点数量。对于DFS,从小华小为位置开始递归搜索能到达的聚餐地点,如果某个聚餐地点能小华和小为访问,则计入可达聚餐地点数量统计;对于BFS,从聚餐地点开始搜索,用一个队列存储当前能够访问的位置,利用队列辅助搜索整个可访问的地图,若小华和小为的位置都被找到则把作为起点的聚餐地点加入可达聚餐地点统计。

解法一:深度优先搜索(DFS)

算法过程

1、遍历地图矩阵,判断当前位置(i,j)是否是2(小华或小为的位置),若是则进入第2步,否则继续下来循环;

2、从当前位置(小华或小为位置)开始进行上下左右四个方向递归搜索,同时维护一个全局visited数组用于记录访问过的位置和一个count变量记录可达地点数目。注意,每轮从一个新用户位置开始搜索时要把visited数组重置,因为新用户可能会走前面用户走过的位置。 遇到边界或已访问过或数值为1的位置回溯,遇到聚餐地点3的位置时用map记录下并更新访问数+1,这个map是全局维护的聚餐地点可达人数,key是聚餐位置坐标,value是可达人数,当前value等于2时给count+1;

3、最后返回count,即题目要求的小华和小为可以到达的聚餐地点数量。

算法可视化

参考代码:

function solution(lines) {
    const arr = lines.trim().split('\n');
    const [m, n] = arr[0].trim().split(' ').map(Number);
    const mtx = arr.slice(1).map(line => line.trim().split(' ').map(e => parseInt(e)));
    
    let poies = new Map();
    let count = 0;
    const visited = new Array(m).fill(0).map(() => new Array(n).fill(false));
    
    let dfs = function(i, j) {
    	if (i<0 || i>=m || j<0 || j>=m || mtx[i][j] === 1) return;
        if (visited[i][j]) return;
        visited[i][j] = true;
    
    	if (mtx[i][j] === 3) {
           if (poies.get(`${i}-${j}`) === undefined) {
              poies.set(`${i}-${j}`, 1);
    	   } else {
    		  poies.set(`${i}-${j}`, 2);
              count++;
    	   }
    	}

        dfs(i+1, j);
        dfs(i, j+1);
        dfs(i-1, j);
        dfs(i, j-1);
    };
    
   
    for (let i = 0; i < m; i++ ) {
       for (let j = 0; j < n; j++) {
           if (mtx[i][j] === 2) {
    		   visited.forEach(item=>{
                  item.fill(false);
    		   });
               dfs(i, j);
    	   }
       }
    }

    return count;
}

let inputs = [
   `4 4
2 1 0 3
0 1 2 1
0 3 0 0
0 0 0 0`,
    `4 4
2 1 2 3
0 1 0 0
0 1 0 0
0 1 0 0`
];

inputs.forEach(line => {
    console.log(solution(line));
});

解法二:广度优先搜索(BFS)

算法过程:

1、定义count变量记录可达聚餐地点数目,定义visited数组记录标记访问过的位置,定义辅助队列queue;

2、遍历地图矩阵筛选数值为3的聚餐地点并加入队列queue中,重置visited数组,定义一个变量userCount记录访问的用户数,准备第3步广度搜索;

3、判断队列queue不为空进入循环,从队列出队当前位置(i, j),判断是否为小华或小为,如果是则userCount++。当userCount为2时更新count+1并退出当前循环,否则继续把上下左右可以访问的位置加入队列,继续下一轮循环;

4、重复2、3的操作完成搜索,count即最终可达聚餐地点的数量。

参考代码:

function solution(lines) {
    const arr = lines.trim().split('\n');
    const [m, n] = arr[0].trim().split(' ').map(Number);
    const mtx = arr.slice(1).map(line => line.trim().split(' ').map(e => parseInt(e)));
    let count = 0;
    let userCount = 0;
    const queue = [];
    const visited = new Array(m).fill(0).map(() => new Array(n).fill(false));
    for (let i = 0; i < m; i++) {
       for (let j = 0; j < n; j++) { 	   
    	   if (mtx[i][j] === 3) {
               visited.forEach(item => {
                  item.fill(false);
    	       });
               userCount = 0; // 访问到的用户数重置
    		   queue.push([i,j]);
    		   while (queue.length) {
    			   let [x,y] = queue.shift();
    			   visited[x][y] = true;
    			   if (mtx[x][y] === 2) {
                       userCount++; // 更新访问到的用户数
    			   }
    			   if (userCount === 2) {
                       count++;
    				   queue.length = 0; // 队列清空
    				   break;
    			   }
    			   if (x-1 >= 0 && mtx[x-1][y] !==1 && !visited[x-1][y]) {
                       queue.push([x-1,y]);
    			   }
    			   if (x+1 < m && mtx[x+1][y] !==1 && !visited[x+1][y]) {
                       queue.push([x+1,y]);
    			   }
    			   if (y-1 >= 0 && mtx[x][y-1] !==1 && !visited[x][y-1]) {
                       queue.push([x,y-1]);
    			   }
    			   if (y+1 < n && mtx[x][y+1] !==1 && !visited[x][y+1]) {
                       queue.push([x,y+1]);
    			   }
    
    		   }
    	   }
       }  
    }
    return count; 
}


let inputs = [
   `4 4
2 1 0 3
0 1 2 1
0 3 0 0
0 0 0 0`,
    `4 4
2 1 2 3
0 1 0 0
0 1 0 0
0 1 0 0`
];

inputs.forEach(line => {
    console.log(solution(line));
});

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值