一、题目描述
二、算法分析说明与代码编写指导
法一
对每个点 S 和 A 各做一次 BFS 求得单源最短路。用求得的全部结果建图,求解最小生成树。
法二
用一次带优先队列的 BFS 代替暴力 n 次 BFS。原理是:
从起点开始做 BFS。找到第一个 A(设到 S 的距离为 D)后,记这个 A 点到它本身距离为 0(到其它点的距离未知),压入优先队列 q。于是,接下来出队的总是 A 及其周围的距离 A 小于 D 的可达点,而其它点都不会出队。
实现时要注意,队列的每个节点都带有起始节点成员,用两个坐标 sx 和 sy 表示。当点 (x, y) 有外星人 A(可以不包括起点 S,因为 S 到每个 A 都会连一条边,不影响最终建图)时,连一条对应起点 <(sx, sy), (x, y)> 的有向边(边的方向不影响最后求解最小生成树)。然后将这点的距离成员 d 标记为 0 再压入队列,而不要直接在 d 数组上标记,否则会因为 d 被标记为 0,从而该点到最近的 A 点的距离无法再更新,导致建图不完全。同样,取某点的距离 D 时,要取队首元素内存储的距离成员 d 的值,否则会导致取到的距离偏大,因为每个 A 点的位置在 d 数组中的值都不是 0。
最后的 d 数组中,每个点对应的值是到最近的 A 点的距离大小。
BFS 完成后,建图就完成了,直接套模板求最小生成树。
本代码中,为与题目给的坐标意义切合(y 行 x 列字符),二维数组的第一个点是纵坐标。
三、AC 代码(0 ms)
法二:
#include<cstdio>
#include<algorithm>
#include<queue>
#pragma warning(disable:4996)
using namespace std;
template<size_t n> class union_find {
private:
unsigned root[n]; int rank[n];
public:
union_find<n>() {
init(); }
union_find<n>(const bool& WannaInit) {
if (WannaInit == true)init(); }
void init() {
fill(rank, rank + n, 1); for (unsigned i = 0; i < n; ++i)root[i] = i;
}
void init(const size_t& _n) {
fill(rank, rank + _n, 1); for (unsigned i = 0; i < _n; ++i)root[i] = i;
}
unsigned find_root(const unsigned& v) {
unsigned r = v, t = v, u;
if (t == root