Boruvka 最小生成树

思想及流程

Boruvka 是 Kruskal 和 Prim 两者的结合算法,“博采众长”因此能轻松解决一类两者很难快速解决的问题,尤其是完全图(稠密图)的 MST。

Boruvka 的思想是:最开始每个点都是一个孤立的连通块,之后经过多轮的迭代,向 MST 边集中加边,把边的两端的连通块合并。加边的方法是:对每个连通块找到离它最近的连通块,这条边叫做这个连通块的“最小边”,每一轮结束时将所有拥有最小边的连通块的最小边加入 MST 边集;这一步可以用 O(E) 地枚举边并更新两端连通块的最小边来做到,但很多时候也会采用 dp 等方法直接求得这个离它最近的连通块。每一轮结束后,所有连通块都设为“没有最小边”,并进入下一轮。对于一个连通图来说,合并一轮下来,都会将连通块的数量至少减半,极端情况是 V/2 个连通块和另外 V/2 个连通块刚好分别是 V/2 个最小边的两端。这样一来,最多只会迭代 logV 轮,常见时间复杂度为 O(ElogV)。(当然主要取决于找最小边的方法)

推荐题目

CODE-FESTIVAL-2017-FINAL Tree MST

这个题就是典型的使用了 dp 的方法来寻找连通块的最近连通块。在每轮中,我们可以用树形 dp 维护一个点的子树里离它最近的两个颜色互异的点(最近的含义是“完全图”中边权最小)。这样一来,这两个点中至少一个的颜色和 x 不一样,而这样的信息也容易为父亲所用。再进行一道换根 dp,得到离每个点最近的、跟这个点颜色不一样的点,用“点的最小边”更新点所在连通块的最小边,照搬 Boruvka 的流程即可。

复制
/*
Todo list:
1. How to embed 'whether a node has miniedge' into ppp
2. How to connect in BCJ
3. You haven't changed vertex to color! DONE
*/
#include <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
#define ppp pair<pii,pii >
#define fi first
#define se second
#define mkp make_pair
using namespace std;
const int N=2e5+5,INF=1e17;
const ppp nu=mkp(mkp(INF,0),mkp(INF,0));
int n,n_v,n_e,ans,w[N],fa[N];
bool bk[N];
vector<pii>gr[N],G[N];
vector<ppp>f[N]; // nearest 2 colors in x's subtree
pair<pii,int>mn[N]; // to store the miniedges for each routine
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void unite(int x,int y){fa[find(y)]=find(x);}
inline void adde(int u,int v,int w){
gr[u].push_back(mkp(v,w)),gr[v].push_back(mkp(u,w));
}
void dfs0(int x,int p){
for(int i=0;i<gr[x].size();i++){
int y=gr[x][i].first,z=gr[x][i].second;
if(y^p)G[x].push_back(mkp(y,z)),dfs0(y,x);
}
}
inline pair<pii,int>gm(pair<pii,int>a,pair<pii,int>b){
return a.se<b.se?a:b;
}
inline ppp merge(ppp a,ppp b){
vector<pii>tmp;
tmp.push_back(a.fi),tmp.push_back(a.se),tmp.push_back(b.fi),tmp.push_back(b.se);
sort(tmp.begin(),tmp.end(),[](pii a,pii b){return a.fi<b.fi;});
ppp c;
c.fi=tmp[0];
if(tmp[0].se!=tmp[1].se)c.se=tmp[1];
else c.se=tmp[2];
return c;
}
void de(ppp a){
printf("(%lld %lld, %lld %lld)\n",a.fi.fi,a.fi.se,a.se.fi,a.se.se);
}
void dfs1(int x){
vector<ppp>pre,suf;
f[x].resize(G[x].size()+1),pre.resize(G[x].size()),suf.resize(G[x].size());
for(int i=0;i<G[x].size();i++){
int y=G[x][i].first,z=G[x][i].second;
dfs1(y);
ppp tmp=f[y][G[y].size()],tmp2=mkp(mkp(w[y],find(y)),mkp(INF,0));
tmp=merge(tmp,tmp2);
tmp.fi.fi+=z,tmp.se.fi+=z;
pre[i]=(!i)?tmp:merge(pre[i-1],tmp);
}
for(int i=(int)G[x].size()-1;~i;i--){
int y=G[x][i].first,z=G[x][i].second;
ppp tmp=f[y][G[y].size()],tmp2=mkp(mkp(w[y],find(y)),mkp(INF,0));
tmp=merge(tmp,tmp2);
tmp.fi.fi+=z,tmp.se.fi+=z;
suf[i]=i==G[x].size()-1?tmp:merge(suf[i+1],tmp);
}
for(int i=0;i<G[x].size();i++){
f[x][i]=merge((!i)?nu:pre[i-1],i==G[x].size()-1?nu:suf[i+1]);
}
f[x][G[x].size()]=G[x].size()?suf[0]:nu;
}
void dfs2(int x,int p,int pe,int pid){
if(p){
ppp tmp=f[p][pid],tmp2=mkp(mkp(w[p],find(p)),mkp(INF,0));
tmp=merge(tmp,tmp2);
tmp.fi.fi+=pe,tmp.se.fi+=pe;
for(int i=0;i<=G[x].size();i++){
f[x][i]=merge(f[x][i],tmp);
}
}
// printf("%lld: ",x),de(f[x][G[x].size()]);
if(f[x][G[x].size()].fi.fi<INF&&f[x][G[x].size()].fi.se!=find(x))mn[find(x)]=gm(mn[find(x)],mkp(mkp(find(x),f[x][G[x].size()].fi.se),f[x][G[x].size()].fi.fi+w[x]));
else if(f[x][G[x].size()].se.fi<INF&&f[x][G[x].size()].se.se!=find(x))mn[find(x)]=gm(mn[find(x)],mkp(mkp(find(x),f[x][G[x].size()].se.se),f[x][G[x].size()].se.fi+w[x]));
for(int i=0;i<G[x].size();i++){
int y=G[x][i].first,z=G[x][i].second;
dfs2(y,x,z,i);
}
}
void Boruvka(){
n_v=n_e=0;
for(int i=0;i<=n;i++)f[i].clear(),mn[i]=mkp(mkp(0,0),INF);
dfs1(1),dfs2(1,0,0,0);
for(int i=1;i<=n;i++)if(find(mn[i].fi.fi)!=find(mn[i].fi.se))unite(mn[i].fi.fi,mn[i].fi.se),ans+=mn[i].se/*,printf("%lld %lld %lld\n",e[i].fi.fi,e[i].fi.se,e[i].se)*/;
// puts("......");
for(int i=1;i<=n;i++)bk[find(i)]=1;
for(int i=1;i<=n;i++)n_v+=bk[i],bk[i]=0;
}
inline int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x;
}
signed main(){
// freopen("input.in","r",stdin);freopen("output.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)w[i]=read(),fa[i]=i;
for(int i=1,u,v,_w;i<n;i++){
u=read(),v=read(),_w=read();
adde(u,v,_w);
}
dfs0(1,0);
n_v=n;
for(int i=1;n_v>1;i++)Boruvka();
cout<<ans;
}
posted @   pengyule  阅读(424)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示