正题
第一题:货车运输
这道题很经典啊~~
直接建立倍增关系式求LCA即可。
不妨设i的第2^j个爸爸是f[i][j],而这条路径上的最小值设为mmin[i][j]。
f[i][j]=f[f[i][j-1](i的2^(j-1)次方爸爸)][j-1]的第j-1次方个爸爸。
那么mmin[i][j]=min(mmin[i][j-1],mmin[f[i][j-1]][j-1]);
所以说我们先把当前的x和y跳到同一层,然后再一起往上跳。
这题那么经典肯定有坑点咯~
1.要建立最大生成树后求解
2.要考虑在不同树的情况
代码<带讲解>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
struct edge{
int y,c,next;
}s[100010];
struct used{
int x,y,c;
}p[50010];
int n,m;
int len=0;
int first[10010];
int f[10010];
int fa[10010][21];
int mmin[10010][21];
int dep[10010];
int findpa(int x){
if(f[x]!=x) return f[x]=findpa(f[x]);
return x;
}
bool cmp(used x,used y){
return x.c>y.c;
}
void ins(int x,int y,int c){
len++;
s[len].y=y;s[len].c=c;s[len].next=first[x];first[x]=len;
}
void dfs(int x){//dfs找以x根的子树集合
for(int i=first[x];i!=0;i=s[i].next){
int y=s[i].y;
if(y!=fa[x][0]){//有边并且不是爸爸
fa[y][0]=x;//更新他的爸爸
dep[y]=dep[x]+1;//深度加一
mmin[y][0]=s[i].c;//最小值更新
dfs(y);
}
}
}
int find_min(int x,int y){
int nowmin=1e9;
if(dep[x]>dep[y]){//优先把y放在底层
int t=x;x=y;y=t;
}
for(int i=20;i>=0;i--)//让y往上跳到与x同样高度
if(dep[fa[y][i]]>=dep[x]) {//可以跳就跳
nowmin=min(nowmin,mmin[y][i]);//更新min
y=fa[y][i];//更新位置
}
if(x==y) return nowmin;
for(int i=20;i>=0;i--)
if(fa[x][i]!=fa[y][i]){//父亲不一样才能跳,要调到lca(x,y)的下一层(lca是最近公共祖先)
nowmin=min(nowmin,min(mmin[x][i],mmin[y][i]));
x=fa[x][i],y=fa[y][i];
}
nowmin=min(nowmin,min(mmin[x][0],mmin[y][0]));//再往上跳一步即可
return nowmin;
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++){
int x,y,c;
scanf("%d %d %d",&x,&y,&c);
if(x==y) continue;
p[i].x=x;p[i].y=y;p[i].c=c;//存边,x,y,c
}
sort(p+1,p+1+m,cmp);//按边权从大到小排序
for(int i=1;i<=n;i++)
f[i]=i;//并查集维护当前点所在集合
for(int i=1;i<=m;i++){
int fx=findpa(p[i].x),fy=findpa(p[i].y);//找当前集合祖先
if(fx!=fy){//不再同一集合
f[fx]=fy;
ins(p[i].x,p[i].y,p[i].c);//加边并且把两个集合合并一起。
ins(p[i].y,p[i].x,p[i].c);
}
}
for(int i=1;i<=n;i++){
if(dep[i]!=0) continue;//被遍历过就选下一个。
dep[i]=1;//没有被遍历过就当根。
dfs(i);//深搜
fa[i][0]=i;//更新父节点(无用)
mmin[i][0]=1e9;//更新最小值(无用)
}
for(int j=1;j<=20;j++)
for(int i=1;i<=n;i++){
fa[i][j]=fa[fa[i][j-1]][j-1];
mmin[i][j]=min(mmin[i][j-1],mmin[fa[i][j-1]][j-1]);
}
int x,y;
int k;
scanf("%d",&k);
for(int i=1;i<=k;i++){
scanf("%d %d",&x,&y);
if(findpa(x)!=findpa(y))//不在同一个集合,那就输出-1(不可能到达)
printf("-1\n");
else
printf("%d\n",find_min(x,y));//如果不是那就输出路径上最小值
}
}