思路
观察题目,经过思考,发现两个点之间最小值最大的路径一定在最大生成树上。 所以我们可以先用kruskal算法求出最大生成树。 之后问题转化为链上最小值。 首先我们可以以1为根建树,之后用倍增算法。dadi,j表示i的2j的父亲,maxi,j表示i到他的2j的父亲路径上所有边的最小权值。 这样我们在求lca的同时可以求出链上最小值。 时间复杂度O(mlogm),空间复杂度O(nlogn)。
代码
//by ziwan Catherine
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define inf 999999999
using namespace std;
int n,m,cnt,head[10005],q;
bool v[10005];
int f[10005],deep[10005];
int fa[10005][21],w[10005][21];
struct edge{
int x,y,dis;
}e[100005];
struct edge2{
int to,next,w;
}ed[100005];
bool cmp(edge x,edge y){
return x.dis>y.dis;
}
void add(int x,int y,int w){//存储最长路的图
ed[++cnt].to=y;
ed[cnt].next=head[x];
ed[cnt].w=w;
head[x]=cnt;
}
int find(int x){
if(f[x]==x) return f[x];
return f[x]=find(f[x]);
}
void kruskal(){
sort(e+1,e+m+1,cmp);
for(int i=1;i<=n;i++)
f[i]=i;//并查集初始化
for(int i=1;i<=m;i++)
if(find(e[i].x)!=find(e[i].y)){
int X=find(e[i].x),Y=find(e[i].y);
f[X]=Y;
add(e[i].x,e[i].y,e[i].dis);
add(e[i].y,e[i].x,e[i].dis);//无向图
}
}
void dfs(int x){
v[x]=1;
for(int i=head[x];i;i=ed[i].next){
int y=ed[i].to;
if(v[y]) continue;
deep[y]=deep[x]+1;
fa[y][0]=x;
w[y][0]=ed[i].w;
dfs(y);
}
}
int do_lca(int x,int y){
if(find(x)!=find(y)) return -1;//不连通
int ans=inf;
if(deep[x]>deep[y]) swap(x,y);//保证y节点更深
for(int i=20;i>=0;i--)//将y提到x节点相同深度 【注意是>=0】
if(deep[fa[y][i]]>=deep[x]){
ans=min(ans,w[y][i]);//更新最大载重(最小边权)
y=fa[y][i];//y向上提
}
if(x==y) return ans;
for(int i=20;i>=0;i--) //寻找公共祖先 【注意是>=0】
if(fa[x][i]!=fa[y][i]){
ans=min(ans,min(w[x][i],w[y][i]));
x=fa[x][i];
y=fa[y][i];
}
ans=min(ans, min(w[x][0], w[y][0]));
//更新此时x,y到公共祖先最大载重,fa[x][0], fa[y][0]即为公共祖先
return ans;
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>e[i].x>>e[i].y>>e[i].dis;
kruskal();
for(int i=1;i<=n;i++)
if(!v[i]){//从每一个根节点进行搜索求出节点深度
deep[i]=1;
dfs(i);
fa[i][0]=i;
w[i][0]=inf;
}
for(int i=1;i<=20;i++)//LCA初始化
for(int j=1;j<=n;j++){
fa[j][i]=fa[fa[j][i-1]][i-1];
w[j][i]=min(w[j][i-1],w[fa[j][i-1]][i-1]);
}
cin>>q;
for(int i=1;i<=q;i++){
int a,b;
cin>>a>>b;
cout<<do_lca(a,b)<<endl;
}
return 0;
}