题目大意:有N个星球,任意两个星球之间的距离为1,因为一次爆炸事故,有M种路被炸坏了,现在给出S和T,求S到T的最短距离
解题思路:如果直接bfs就挂了,每个点只需要被遍历一次就够了,这个可以用并查集解决,用f[i]表示下一个结点要从哪开始
用尺取法的思想,从小到大,依次判断一下能否到达该点,如果可到达的话,那么距离就加1,并将该点并入。
如果不行的话,找寻下一个点,并判断能否到达即可
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int N = 100010;
struct Forbidden{
int v1, v2;
Forbidden() {}
Forbidden(int v1, int v2): v1(v1), v2(v2) {}
};
vector<Forbidden> G[N];
int n, m;
int f[N], vis[N];
bool cmp(const Forbidden &a, const Forbidden &b) {
return a.v1 < b.v1;
}
void init() {
scanf("%d%d", &n, &m);
for (int i = 0; i <= n; i++) {
G[i].clear();
f[i] = i;
}
while (m--) {
int u, v1, v2;
scanf("%d%d-%d", &u, &v1, &v2);
if (v1 > v2) swap(v1, v2);
G[u].push_back(Forbidden(v1, v2));
}
for (int i = 0; i < n; i++)
sort(G[i].begin(), G[i].end(), cmp);
}
int cas = 1;
int find(int x) {
return x == f[x] ? x : f[x] = find(f[x]);
}
int bfs(int s, int e) {
queue<int> Q;
Q.push(s);
vis[s] = 0;
while (!Q.empty()) {
int u = Q.front(); Q.pop();
//t表示第几个限制,v表示从哪开始走
int t = 0, v = find(0);
if (u == e) return vis[u];
while (v < n) {
//如果要走的点刚好时该点的话,就找寻下一个点
if (v == u) v = find(v + 1);
bool flag = false;
for (int i = t; i < G[u].size(); i++) {
if (v >= G[u][i].v1 && v <= G[u][i].v2) {
flag = true;
//找寻这个区间外的下一个点
v = find(G[u][i].v2 + 1);
t = i + 1;
break;
}
}
//可行的点,距离+1,并将其并入下一个点,这样find该点的时候,就不会重复走了
if (!flag) {
vis[v] = vis[u] + 1;
Q.push(v);
f[v] = find(v + 1);
v = find(v);
}
}
}
return -1;
}
void solve() {
int s, t;
scanf("%d%d", &s, &t);
printf("Case #%d: ", cas++);
int ans = bfs(s, t);
if (ans == -1) printf("Impossible\n");
else printf("%d\n", ans);
}
int main() {
int test;
scanf("%d" ,&test);
while (test--) {
init();
solve();
}
return 0;
}