求任意两点之间的最短距离,本应该用最短路径算法。
但这题明确了图不含环,某些点之间可能没有边。
所以给出的图就可以转化为一片森林,对于每棵树,指定一个根节点root,dis[i]表示点i到root的距离,
任意两点i和j的最短距离 = dis[i] + dis[j] - 2 * dis[LCA(i, j)],
所以可以改用“并查集 + LCA”的方法
但这题明确了图不含环,某些点之间可能没有边。
所以给出的图就可以转化为一片森林,对于每棵树,指定一个根节点root,dis[i]表示点i到root的距离,
任意两点i和j的最短距离 = dis[i] + dis[j] - 2 * dis[LCA(i, j)],
所以可以改用“并查集 + LCA”的方法
并查集用于判断给定的两个点是否连通。
查阅过网上很多人给出的算法都是LCA离线算法,如今就只给出LCA的在线算法。
#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
#define maxn 10001
using namespace std;
typedef struct NODE{
int v, len;
NODE(int _v=0, int _len=0):v(_v), len(_len){}
bool operator < (const struct NODE& rhs) const{ return len < rhs.len; }
} node;
vector<node> edge[maxn];
int dis[maxn];
int H[maxn], fa[maxn]; //点的高度,父结点
void dfs(int u, int h, int father){
H[u] = h;
fa[u] = father;
for(int i=0; i<edge[u].size(); i++){
node& e = edge[u][i];
if(!fa[e.v]){
dis[e.v] = dis[u] + e.len;
dfs(e.v, h+1, u);
}
}
}
void swap(int& a, int& b){
int t = a; a = b; b = t;
}
int LCA(int p, int q){
if(H[p] < H[q])
swap(p, q); //确保p比q深,即p的高度大于q的高度
while(H[p] > H[q]) p = fa[p]; //将p提升至与q同一高度
if(p == q) return p;
while(p != q) p = fa[p], q = fa[q]; //p和q一起提升直到两者"相遇"
return p;
}
int solve(int p, int q){
int tmp = LCA(p, q);
return dis[p] + dis[q] - 2 * dis[tmp];
}
int f[maxn]; //并查集
int find(int x){
return f[x]==x ? x : f[x]=find(f[x]);
}
int main(){
freopen("input.txt", "r", stdin);
int n; //n个点
int m; //m条边
int c; //c次询问
int a, b, k; //点a和点b间的距离是k
while(scanf("%d %d %d", &n, &m, &c) != EOF){
int i;
for(i=1; i<=n; i++) edge[i].clear(), f[i] = i;
while(m--){
scanf("%d %d %d", &a, &b, &k);
edge[a].push_back( node(b, k) );
edge[b].push_back( node(a, k) );
int x = find(a), y = find(b);
if(x<y) f[y] = x; else f[x] = y;
}
memset(H, 0, sizeof(H));
memset(fa, 0, sizeof(fa));
for(int j=1; j<=n; j++)
if(f[j]==j){
dis[j] = 0;
dfs(j, 1, -1); //点j是根,高度为1,求出连通分量其它点的高度,父结点
}
int p, q;
while(c--){
scanf("%d %d", &p, &q);
if(find(p) != find(q)) //p和q不在同一个连通分量
printf("Not connected\n");
else{
int ans = solve(p, q);
printf("%d\n", ans);
}
}
}
return 0;
}