倍增求LCA+最大生成树
题目给出的是一张图,在图上有很多算法无法实现,所以要将其转化为树
题中可以发现货车的最后的载重量是由权值最小的一条边决定的,所以我们求最大生成树
求完最大生成树后我们得到一个森林
现在转化为了求两点路径经过边的边权的最小值,用倍增算法进行计算
#include <bits/stdc++.h> #define inf 1e9 using namespace std; const int MAXN=10100; int n,m,first[MAXN],nxt[MAXN*10],point[MAXN*10],len[MAXN*10]; int q,tot,fa[MAXN],f[MAXN][21],chmin[MAXN][21],vi[MAXN],de[MAXN]; struct node { int u,v,len; }sh[MAXN*5]; void add_edge(int x,int y,int z) { tot++; nxt[tot]=first[x]; first[x]=tot; point[tot]=y; len[tot]=z; } void dfs(int x,int father,int deep,int last,int visit) { de[x]=deep; vi[x]=visit; f[x][0]=father;//倍增father数组 chmin[x][0]=last;//记录上一条边的权值 for (int i=first[x];i!=-1;i=nxt[i]) { if (point[i]!=father) { dfs(point[i],x,deep+1,len[i],visit); } } } int getfather(int x) { if (fa[x]==x) return fa[x]; fa[x]=getfather(fa[x]); return fa[x]; } bool cmp(node a,node b) { return a.len>b.len; } int lca(int a,int b)//倍增求LCA { if (getfather(a)!=getfather(b)) return -1; if (de[a]>de[b]) swap(a,b); int MIN=inf; for (int i=20;i>=0;i--) { if (de[f[b][i]]>=de[a]) { MIN=min(MIN,chmin[b][i]); b=f[b][i]; } } if (a==b) return MIN; for (int i=20;i>=0;i--) { if (f[a][i]!=f[b][i]) { MIN=min(MIN,min(chmin[a][i],chmin[b][i])); a=f[a][i]; b=f[b][i]; } } MIN=min(MIN,min(chmin[a][0],chmin[b][0])); return MIN; } int main() { tot=-1; memset(nxt,-1,sizeof(nxt)); memset(first,-1,sizeof(first)); scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { scanf("%d%d%d",&sh[i].u,&sh[i].v,&sh[i].len); } for (int i=1;i<=n;i++) fa[i]=i; sort(sh+1,sh+1+m,cmp); for (int i=1;i<=m;i++) { if (getfather(sh[i].u)!=getfather(sh[i].v)) { fa[getfather(sh[i].u)]=getfather(sh[i].v); add_edge(sh[i].u,sh[i].v,sh[i].len); add_edge(sh[i].v,sh[i].u,sh[i].len); } }//求最大生成树 int now=0; for (int i=1;i<=n;i++)//可能有多棵树,每棵树分别考虑 { if (vi[i]!=0) continue; now++; dfs(i,0,0,inf,now); f[i][0]=i; chmin[i][0]=inf; } for (int j=1;j<=20;j++) { for (int i=1;i<=n;i++) { f[i][j]=f[f[i][j-1]][j-1]; chmin[i][j]=min(chmin[i][j-1],chmin[f[i][j-1]][j-1]);//进行倍增 } } scanf("%d",&q); for (int i=1;i<=q;i++) { int x,y; scanf("%d%d",&x,&y); printf("%d ",lca(x,y)); } }
倍增求LCA时避免父亲倍增数组出现0,倍增记录最小值时,简单写法
int now=0; for (int i=1;i<=n;i++) { if (vi[i]!=0) continue; now++; dfs(i,0,0,inf,now); f[i][0]=i;//使当前的father==i chmin[i][0]=inf;//记录的最小值 }
求deep时,注意不是求距离上的deep,而是层数
void dfs(int x,int father,int deep,int last,int visit) { de[x]=deep; vi[x]=visit; f[x][0]=father; chmin[x][0]=last; for (int i=first[x];i!=-1;i=nxt[i]) { if (point[i]!=father) { dfs(point[i],x,deep+1,len[i],visit);//deep是+1,不是加len[i],注意 } } }