华为笔试题:K小姐的魔法镜阵/信息传输—广度优先搜索(基于时间截的)

 希望我们不仅把编程当成一份工作,更要将其当成一份热爱!!!

                                                                                -----潜意识中有个想成为一名厉害的程序员的梦

问题描述(137. 消息传输 (kamacoder.com)

在给定的 m x n (1 <= m, n <= 1000) 网格地图 grid 中,分布着一些信号塔,用于区域间通信。

每个单元格可以有以下三种状态:

  • 值 0 代表空地,无法传递信号;

  • 值 1 代表信号塔 A,在收到消息后,信号塔 A 可以在 1ms 后将信号发送给上下左右四个方向的信号塔;

  • 值 2 代表信号塔 B,在收到消息后,信号塔 B 可以在 2ms 后将信号发送给上下左右四个方向的信号塔。

给定一个坐标 (j, k),输入保证坐标 (j, k) 位置一定有信号塔。在坐标 (j, k) 位置的信号塔触发一个信号。

要求返回网格地图中所有信号塔收到信号的最短时间,单位为 ms。如果有信号塔无法收到信号,则返回 -1。

输入描述

  • 第一行:网格的列数 n

  • 第二行:网格的行数 m

  • 第三行:触发信号的信号塔坐标 (j, k)

  • 接下来的 m:每行包含 n 个整数,表示该行网格中每个位置的信号塔安装信息(通过空格间隔每个状态值)。

输出描述

输出返回网格地图中所有信号塔收到信号的最小时间,单位为 ms。如果不可能,返回 -1。

输入示例

3
3
1 0
0 1 2
1 2 1
0 1 2

输出示例

4

解题思路

广度搜索思想

按照时间为基准,求1s能到达的所有信号塔、然后递推2s能到达的所有信号塔、3s......,并记录到达每个信号塔的时间
  1. 时间基础变量:用于记录当前时间。

  2. 信号传播:先求1秒时能传递到的 所有信号塔 及其到达时间,再求2秒的,再求3秒的。这样求出的结果对每一个信号塔都是最短时间。

  3. 最大时间:其中所有信号塔中的最大时间,就是所有信号塔被传递信号的最小时间。

每一次循环中的操作
  1. 队列中的元素:代表当前时刻信号传递到的信号塔集合(例如:1s时传递到的信号塔们,2s时传递到的信号塔们)。

  2. 推测下一秒钟信号传递到的信号塔

    • 对于当前队列中的为1的信号塔,它下一秒就会向外辐射,我们对其进行辐射操作

    • 对于当前队列中的为2的信号塔,判断 信号到达它的时间 是否是当前时间(时间基准)。

      • 如果是,则不向外辐射(因为它要经过2s后才会向外辐射)

      • 如果当前时间大于 它的到达时间1s,则向外辐射(因为它2s后辐射,当前已经过了1s,下一秒就会辐射)

  3. 处理完当前队列的信号塔后,也就推测出了下一秒到达的所有信号塔,然后

    • 时间基础变量加1。

  4. 当广度优先搜索完成后,查看每个信号塔的到达时间,如果其到达时间点为0(0是初始化的值),则代表它没有被辐射到,则输出-1

代码(存在部分冗余,懒得改了)

#include<iostream>
#include<vector>
#include<list>
using namespace std;
int main()
{
	vector<vector<int>> dir = { {1,0} ,{-1,0}, {0,1}, {0,-1} };
	int n, m;
	cin >> n;
	cin >> m;
	vector<vector<int>> grap(m, vector<int>(n, 0));//信号塔的值
	vector<vector<bool>> visited(m, vector<bool>(n, false));//是否被信号传递到过
	vector<vector<int>> times(m, vector<int>(n, 0));//每个信号塔被传递到的最小时间
	int x, y;
	cin >> x;
	cin >> y;

	for (int i = 0; i < m; i++)
		for (int j = 0; j < n; j++)
		{
			cin >> grap[i][j];
		}

	list<pair<int, int>> my_que;//之所以用list作为队列,每次循环处理过程中:部分值为2的信号塔不会被处理,因此涉及到删除非队列头元素的问题,list更高效。
	my_que.push_back({ x,y });
	visited[x][y] = true;
	int time_now = 0;
	int max_time = 0;
	while (!my_que.empty())
	{
		//每次循环的目的是求 下一秒能到达的所有信号塔,及其他们的到达时间
		int size = my_que.size();
		auto it = my_que.begin();
		for (int j = 0; j < size; j++)
		{
			//对于当前队列中的为1的信号塔,它下一秒就会向外辐射,我们对其进行辐射操作
			if (grap[it->first][it->second] == 1)
			{
				int x = it->first;
				int y = it->second;

				for (int i = 0; i < 4; i++)
				{
					int temp_x = x + dir[i][0];
					int temp_y = y + dir[i][1];
					//越界处理
					if (temp_x < 0 || temp_x >= m || temp_y < 0 || temp_y >= n)
					{
						continue;
					}
					
					if (visited[temp_x][temp_y] == false && grap[temp_x][temp_y] != 0)//被辐射过的点不再辐射,空地不能辐射
					{
						visited[temp_x][temp_y] = true;//标记此信号塔被辐射过
						times[temp_x][temp_y] = times[x][y] + grap[x][y];//求出此信号塔的最小到达时间
						//求所有信号塔的到达时间的最大值
						if (times[temp_x][temp_y] > max_time)
							max_time = times[temp_x][temp_y];

						my_que.push_back({ temp_x,temp_y });
					}
				}
				it = my_que.erase(it);
			}
			//对于当前队列中的为2的信号塔,如果当前时间大于 它的到达时间1s,则向外辐射(因为它2s后辐射,当前已经过了1s,下一秒就会辐射)
			else if (time_now - times[it->first][it->second] == 1)
			{
				int x = it->first;
				int y = it->second;

				for (int i = 0; i < 4; i++)
				{
					int temp_x = x + dir[i][0];
					int temp_y = y + dir[i][1];
					if (temp_x < 0 || temp_x >= m || temp_y < 0 || temp_y >= n)
					{
						continue;
					}
					if (visited[temp_x][temp_y] == false && grap[temp_x][temp_y] != 0)
					{
						visited[temp_x][temp_y] = true;
						times[temp_x][temp_y] = times[x][y] + grap[x][y];
						if (times[temp_x][temp_y] > max_time)
							max_time = times[temp_x][temp_y];
						my_que.push_back({ temp_x,temp_y });
					}
				}
				it = my_que.erase(it);
			}
			//对于当前队列中的为2的信号塔,当前时间等于它的到达时间,则下一秒不会辐射。
			else
			{
				it++;
			}
		}
		time_now++;
	}
	//当广度优先搜索完成后,查看每个信号塔的到达时间,如果其到达时间点为0(0是初始化的值),则代表它没有被辐射到,则输出-1
	for (int i = 0; i < m; i++)
		for (int j = 0; j < n; j++)
		{
			if (i == x && j == y)
				continue;
			if (grap[i][j] != 0)
			{
				if (times[i][j] == 0)
				{
					cout << -1;
					return 0;
				}
			}
		}
	cout << max_time;
	return 0;
}

总结

通过这种方式,我们可以逐步推断出每一秒钟信号传播到的信号塔,最终求出所有信号塔的信号到达的最小时间,所有最小时间的最大值,即为所有信号被传到的最小时间

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值