最小生成树(MST)学习笔记
定义一张图的生成树是我觉得你知道。
Kruskal 算法
此算法求最小生成树,是进行 \(n-1\) 次加边操作,每次挑选当前长度最小的边,然后判断它的两个端点是否已经在同一个连通块内(并查集!),若是则扔,若否则加。如果原图是联通图的话,进行 \(n-1\) 轮操作之后就可以得到最小生成树拉。
代码:
int kruscal()
{
long long sum=0;
for(int i=1;i<=len;i++)
{
int fx=getf(e[i].x),fy=getf(e[i].y);
if(fx==fy)continue;
sum+=e[i].v;f[fx]=fy;
if(++cnt==n-1)return sum;
}
return sum;
}
//但是我一般这么写 qwq:
struct edge{
int x,y,v,id,f;
friend bool operator<(edge a,edge b)
{
return a.v<b.v;
}
}e[M];
void kru()
{
int z=0;
for(int t=1;t<n;t++)
{
while(z<=m)
{
++z;
int fx=getf(e[z].x),fy=getf(e[z].y);
if(fx==fy)continue;
else if(e[z].f)ans[e[z].id]=true;
else break;
}
if(z>m)break;//如果原图不联通,到这里就可以结束了
merge(e[z].x,e[z].y);
}
}
Kruskal 重构树
在用 kru 算最小生成树的时候,我们会依次加入 \(n-1\) 条边,我们把每条边看作一个点,点权为原来的边权,令它连接连接的两个连通块的根,并且它成为新的根。然后我们就得到了一颗新的树,称为 Kruskal 重构树。发现两个点在新树上的 LCA 就是最小生成树上两点路径上的最大边。
Prim 算法
完全图怎么办!可以使用 Prim算法。与 kru 不同的是,Prim 把点分为了两个集合,分为已经在生成树上的(最开始里面可以有 \(1\) 号结点)和其他孤立的点,每次操作选择孤立点集合中到第一个集合中其中某个点距离最近的点加入集合。
代码:
struct edge{
int y,nxt;long long v;
}e[N*N*2];
int lk[N],len=0;
inline void insert(int x,int y,long long v)
{
e[++len].nxt=lk[x];lk[x]=len;
e[len].v=v;e[len].y=y;
}
long long sum=0;
long long d[N];
bool vis[N];
void prim()
{
vis[n]=true;d[n]=0;
for(int i=1;i<n;i++)
{
long long minn=1e10;int k=-1;
for(int j=1;j<=n;j++)
if(!vis[j]&&d[j]<minn)minn=d[j],k=j;
sum+=minn;vis[k]=true;
for(int j=lk[k];j;j=e[j].nxt)
{
if(!vis[e[j].y])d[e[j].y]=min(d[e[j].y],1ll*e[j].v);
}
}
}
其他
一个结论
把原图的边集划分为若干个集合,它们的并为原边集,在这些边集中求最小生成树,再在这些树边中求最小生成树,得到原图的最小生成树。
一道题:
有一棵 \(n\) 个顶点的树,给你一个新图,新图点集不变,两个点之间的边权为树上两点的距离加上两点的点权。求新图的最小生成树。
发现原图为菊花的情况是好求的。考虑点分治,每次把重心和其他点连边,然后对这些边求最小生成树。
如果给你一堆点
告诉你这些点两两之间的距离为 \(min\{|x_i-x_j|,|y_i-y_j|\}\),求这一堆点的最小生成树,怎么办呢?我们只需要把所有点分别按照 \(x,y\) 排序,每次排完序在相邻的点之间连边,然后这样求最小生成树就好了。一道题
次小生成树
枚举没有加到最小生成树的每条边,求出来它两个端点在树上路径上经过的最长边,用这条边替换树上的边求出新的生成树边权和,最后取最小值。
严格次小生成树
依然枚举没有加进生成树里的所有边,找两个端点路径上的最长边和严格次长边,如果这条边和最长边的长度相等,就用它替换次长边,记录答案。
瓶颈生成树
无向图的瓶颈生成树的最大边权在所有生成树中最小。最小生成树是瓶颈生成树的充分不必要条件。
最小瓶颈路
图上 \(x,y\)之间的最小瓶颈路,为从 \(x\) 到 \(y\) 的所有路径中,经过最大边最小的。
性质:两点之间的最小瓶颈路是两点在最小生成树上的路径。
有道题,发现每次询问在树上倍增找最长路太慢了,所以用 kruskal 重构树,变成求 LCA 的问题,什么,你不会很快地求 LCA?
浙公网安备 33010602011771号