链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
众所周知,原魔是全宇宙最好的游戏。最近原魔出了一个策划模拟器,许多魔鬼策划在里面疯狂彰显才能,尽情的虐待旅行者。有这么一个萌新策划,她设置出了一个简单的地图,该地图有n个房间,她给每两个房间之间建了一条通道,该通道可以双向同行,但她觉得这样太简单了,决定加大难度,于是,她就拆掉了其中一些路,并想把终点设置在离起点最远的一间房间里,但她不知道那个最远,于是她向你求助,她告诉你了起点在哪个房间以及她拆掉了那些路,现在请你告诉她每个房间到起点的距离,并且告诉他哪个房间离起点最远,如果某个房间不能到达则输出-1。
输入描述:
第一行输入两个整数n,m,分别代表房间数,拆除的通道数。(n<=2e4,m<=1e5)
第二行输入一个整数S,表示起点房间的编号。(S∈[1,n])
第三行开始m行,每行两个整数a,b,表示房间a与房间b之间的通道被拆除了。(a,b∈[1,n],且a≠b)
输出描述:
输出共两行,第一行输出n个整数,表示每个房间到起点的距离,第二行一个整数,表示离起点最远的那个房间,如果最远的房间有多个,输出编号最小的那个。
示例1
输入
4 2 1 1 4 2 4
4 2 1 1 4 2 4
输出
0 1 1 2 4
0 1 1 2 4
解题思路:
通过本题的描述,很容易看出就是一个简单的bfs能解决的问题,但题目给的是一个补图,要想跑bfs就得建出正图,而建正图的复杂度是O(n^2),有亿点高,题目给的n的范围是2e4,要建正图绝对是超时了,这时我们发现,题目给的m很小,说明补图很小,既然这样,为什么不在补图上跑bfs呢?那又要怎么在补图上bfs呢?在这里我们要用到STL中的set,用set来维护图中还有那些点没跑到,在跑到这个点时,把与这个点在补图中有边的点放到未跑到的点集中,剩下的就是与之相连的点了,接下来就是正常的bfs就行了。
过题代码:
#include<bits/stdc++.h>
using namespace std;
#define Inf 1e9+7
const int N = 2e4 + 10;
const int M = 1e5 + 10;
int n, m;
int S, T;
int e[M], w[M], ne[M], h[N], idx;
int A[N];
int dis[N];
int q[N];
bitset<N>vis;
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
void bfs() {
int hh = 0, tt = 0;
q[0] = S;
set<int>s1, s2;
for (int i = 1;i <= n;i++) {
if (i != S) {
s1.insert(i);
}
}
while (hh <= tt) {
int t = q[hh++];
for (int i = h[t];~i;i = ne[i]) {
int y = e[i];
s1.erase(y);
s2.insert(y);
}
for (set<int>::iterator it = s1.begin();it != s1.end();it++) {
if (vis[*it]) {
continue;
}
vis[*it] = 1;
dis[*it] = dis[t] + 1;
q[++tt] = *it;
}
s1.swap(s2);
s2.clear();
}
}
int main() {
memset(dis, Inf, sizeof(dis));
vis.reset();
memset(h, -1, sizeof(h));
idx = 0;
scanf("%d %d", &n, &m);
scanf("%d", &S);
while (m--) {
int a, b;
scanf("%d %d", &a, &b);
add(a, b);
add(b, a);
}
dis[S] = 0;
bfs();
int ma = -1;
int x = S;
for (int i = 1;i <= n;i++) {
if (dis[i] >= Inf) {
printf("-1 ");
}
else {
printf("%d ", dis[i]);
if (ma < dis[i]) {
ma = dis[i];
x = i;
}
}
}
printf("\n%d\n", x);
}