最小生成树
最小生成树
[生成树] 从一个无向连通图中选取一些边使这张图是一颗树。 [最小生成树] 在生成树的基础上使边权和最小。 [Kruskal] 寻找满足条件的边 贪心,从未选取的边中选一条边权最小的边, 选完后不出环即可。 我们需要判断: 1.当前最小边权的边。 2.这条边所连接的两个点的连通性。 用并查集判断即可。 [Prim] 寻找满足条件的点 在没有加入连通块的点中选一个最近的加入连通块, 我们需要判断: 1.哪些点还没有加入连通块 2.这些点与连通块的距离 3.距离最近的点 eg: 1. accoders【一本通图 最小生成树】最短网络(agrinet)2023 luogu P1546 [USACO3.1] 最短网络 Agri-Net 思路: 最小生成树的板子。 [Kruskal] code: #include<bits/stdc++.h> using namespace std; int n,a[110][110]; struct node{ int l,r,w; bool operator <(const node &ret)const{ return w<ret.w; } }e[1000010]; int cnt,fa[1000100],ans=0,tot=0; int find(int x){ if(fa[x]==x){ return x; } return fa[x]=find(fa[x]); } int main(){ cin>>n; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ cin>>a[i][j]; if(i<j){ e[++cnt]={i,j,a[i][j]}; } } } for(int i=1;i<=n;i++){ fa[i]=i; } sort(e+1,e+cnt+1); for(int i=1;i<=cnt;i++){ int l=e[i].l,r=e[i].r,w=e[i].w; int fl=find(l),fr=find(r); if(fl!=fr){ fa[fl]=fr; tot++; ans+=w; } if(tot==n-1){ break; } } cout<<ans; return 0; } 2. accoders的【一本通图 最小生成树】 局域网 luogu P2820 局域网 思路: 求出最小生成树后用边权总和减去最小生成树的边权和即可。 [Prim] code: #include<bits/stdc++.h> using namespace std; typedef pair<int,int> P; const int N=100010; struct node{ int to,l; }; int n,m,ans,tot; vector<node>mp[N+10]; int vis[N+10],dis[N+10]; void prim(int rt){ priority_queue<P,vector<P>,greater<P> >q; dis[rt]=0; q.push(P(dis[rt],rt)); while(!q.empty()){ int l=q.top().second,w=q.top().first; q.pop(); if(vis[l]){ continue; } vis[l]=1; ans+=w; for(auto v:mp[l]){ if(dis[v.to]>v.l){ dis[v.to]=v.l; q.push(P(dis[v.to],v.to)); } } } } int main(){ cin>>n>>m; memset(dis,0x3f3f3f3f,sizeof dis); for(int i=1;i<=m;i++){ int l,r,w; cin>>l>>r>>w; mp[l].push_back({r,w}); mp[r].push_back({l,w}); tot+=w; } prim(1); cout<<tot-ans; return 0; } [Kruskal] #include<bits/stdc++.h> using namespace std; int n,k,cnt; struct node{ int l,r,w; bool operator <(const node &W)const{ return w<W.w; } }mp[110*110]; int fa[110*110]; int find(int x){ if(fa[x]==x){ return x; } return fa[x]=find(fa[x]); } int main(){ cin>>n>>k; int tot=0,ans=0; for(int i=1;i<=k;i++){ int l,r,w; cin>>l>>r>>w; mp[++cnt]={l,r,w}; tot+=w; } for(int i=1;i<=n;i++){ fa[i]=i; } sort(mp+1,mp+cnt+1); for(int i=1;i<=cnt;i++){ int l=mp[i].l,r=mp[i].r,w=mp[i].w; int fl=find(l),fr=find(r); if(fl!=fr){ fa[fl]=fr; ans+=w; } } cout<<tot-ans; return 0; } 3. accoders的【一本通图 最小生成树】 繁忙的都市 2025 luogu P2330 [SCOI2005] 繁忙的都市 思路: 最小生成树但是要求有多少个边和边权最大的边。 code: [Kruskal] #include<bits/stdc++.h> using namespace std; int n,m,cnt,tot,ma=-1; struct node{ int l,r,w; bool operator <(const node &W)const{ return w<W.w; } }mp[310*310]; int fa[310*310]; int find(int x){ if(x==fa[x]){ return x; } return fa[x]=find(fa[x]); } int main(){ cin>>n>>m; for(int i=1;i<=m;i++){ int l,r,w; cin>>l>>r>>w; mp[++cnt]={l,r,w}; } for(int i=1;i<=n;i++){ fa[i]=i; } sort(mp+1,mp+cnt+1); for(int i=1;i<=cnt;i++){ int l=mp[i].l,r=mp[i].r,w=mp[i].w; int fl=find(l),fr=find(r); if(fl!=fr){ fa[fl]=fr; tot++; ma=max(ma,w); } } cout<<tot<<" "<<ma; return 0; } 4. accoders的【一本通图 最小生成树】联络员(liaison)2026 思路: 先将必选选入,然后从小到大做非必选的最小生成树即可。 code: [Kruskal] #include<bits/stdc++.h> using namespace std; int fa[2001*2001],cnt; struct node{ int l,r,w,fl; bool operator <(const node &W)const{ return w<W.w; } }mp[2001*2001]; int find(int x){ if(x==fa[x]){ return x; } return fa[x]=find(fa[x]); } int main(){ int n,m; cin>>n>>m; int tot=0,ans=0; for(int i=1;i<=m;i++){ int l,r,w,f; cin>>f>>l>>r>>w; mp[++cnt]={l,r,w,f}; } for(int i=1;i<=n;i++){ fa[i]=i; } sort(mp+1,mp+cnt+1); for(int i=1;i<=cnt;i++){ int l=mp[i].l,r=mp[i].r,w=mp[i].w,f=mp[i].fl; int fl=find(l),fr=find(r); if(f==1){ fa[fl]=fr; ans+=w; tot++; } } for(int i=1;i<=cnt;i++){ int l=mp[i].l,r=mp[i].r,w=mp[i].w,f=mp[i].fl; int fl=find(l),fr=find(r); if(fl!=fr&&f==2){ fa[fl]=fr; ans+=w; tot++; } } cout<<ans; return 0; } 5. accoders 的【一本通图 最小生成树】 连接格点2098 思路: 通过类似hash的操作将格点强行建边, 把给的边放入边数组中,然后跑一遍最小生成树。 code(感谢little_sheep_2024的code): #include<bits/stdc++.h> using namespace std; int m,n,f[1000005]; int x,xx,y,yy,idx=0; struct edge{ int u,v,w; }e[5000005]; int find(int u){ return f[u]==u ? u : f[u]=find(f[u]); } int fe(int x,int y){ return m*(x-1)+y; } bool cmp(edge x,edge y){ return x.w<y.w; } int kruscal(){ int ans=0; sort(e+1,e+idx+1,cmp); for(int i=1;i<=idx;i++){ int fu=find(e[i].u); int fv=find(e[i].v); if(fu!=fv){ ans+=e[i].w; f[fu]=fv; } } return ans; } int main(){ cin>>n>>m; for(int i=1;i<=n*m;i++){ f[i]=i; } while(cin>>x>>y>>xx>>yy){ int fu=find(fe(x,y)); int fv=find(fe(xx,yy)); f[fu]=fv; } for(int i=1;i<m;i++){ for(int j=1;j<=n;j++){ e[++idx].u=fe(j,i),e[idx].v=fe(j,i+1); e[idx].w=2; } } for(int i=1;i<n;i++){ for(int j=1;j<=m;j++){ e[++idx].u=fe(i,j),e[idx].v=fe(i+1,j); e[idx].w=1; } } cout<<kruscal(); return 0; } 6. accoders的【一本通提高篇最小生成树】新的开始2098 思路: 将建设发电机的费用变成一个点放在原本的图外, 这样建设发电机就变成了连一个费用为V的边, 然后就成了一个大图的最小生成树。 code: [Kruskal] #include<bits/stdc++.h> using namespace std; const int N=1000010; int n; struct node{ int l,r,w; bool operator <(const node &W) const{ return w<W.w; } }mp[N]; int cnt,fa[N]; int find(int x){ if(fa[x]==x){ return x; } return fa[x]=find(fa[x]); } int main(){ cin>>n; for(int i=1;i<=n+1;i++){ fa[i]=i; } for(int i=1;i<=n;i++){ int v; cin>>v; mp[++cnt]={i,n+1,v}; } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ int w; cin>>w; if(i<j){ mp[++cnt]={i,j,w}; } } } sort(mp+1,mp+cnt+1); int tot=0,ans=0; for(int i=1;i<=cnt;i++){ int l=mp[i].l,r=mp[i].r,w=mp[i].w; int fl=find(l),fr=find(r); if(fl!=fr){ fa[fl]=fr; ans+=w; tot++; } if(tot>=n){ break; } } cout<<ans; return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库