Boruvka 最小生成树
思想及流程
Boruvka 是 Kruskal 和 Prim 两者的结合算法,“博采众长”因此能轻松解决一类两者很难快速解决的问题,尤其是完全图(稠密图)的 MST。
Boruvka 的思想是:最开始每个点都是一个孤立的连通块,之后经过多轮的迭代,向 MST 边集中加边,把边的两端的连通块合并。加边的方法是:对每个连通块找到离它最近的连通块,这条边叫做这个连通块的“最小边”,每一轮结束时将所有拥有最小边的连通块的最小边加入 MST 边集;这一步可以用 地枚举边并更新两端连通块的最小边来做到,但很多时候也会采用 dp 等方法直接求得这个离它最近的连通块。每一轮结束后,所有连通块都设为“没有最小边”,并进入下一轮。对于一个连通图来说,合并一轮下来,都会将连通块的数量至少减半,极端情况是 个连通块和另外 个连通块刚好分别是 个最小边的两端。这样一来,最多只会迭代 轮,常见时间复杂度为 。(当然主要取决于找最小边的方法)
推荐题目
CODE-FESTIVAL-2017-FINAL Tree MST
这个题就是典型的使用了 dp 的方法来寻找连通块的最近连通块。在每轮中,我们可以用树形 dp 维护一个点的子树里离它最近的两个颜色互异的点(最近的含义是“完全图”中边权最小)。这样一来,这两个点中至少一个的颜色和 不一样,而这样的信息也容易为父亲所用。再进行一道换根 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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?