- admin's blog
【模板】最小生成树
- @ 2024-1-7 10:53:21
【模板】最小生成树
在无向图中,将所有点联通起来所需要的最小花费的树
Kruscal
贪心取边,能取最小边就取最小边
算法思路
- 对于一颗树,如果他有n个节点,那么肯定连了n-1条边
- 要使这颗树的边权之和最小,就贪心取所有边中的小边,而每取一条边就将边链接的两点归为同一个集合(并查集),而在取之前两个节点就是一个集合的话,这条边就没有贡献,就不取这条边。
算法流程
- 建图、连边
- 按边权排序
- 贪心取边并判是否在同一集合
Code
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+12;
int fa[N],n,m,ans,cnt;
struct node{
int u,v,w;
}e[N];
int cmp(node a,node b)
{
return a.w<b.w;
}
int f(int x)
{
if( x!=fa[x] ) return fa[x]=f(fa[x]);
return fa[x];
}
void k()
{
sort(e+1,e+1+m,cmp);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++)
{
if(cnt==n-1) break;
int a=f(e[i].v);
int b=f(e[i].u);
if( a==b ) continue;
fa[a]=b,cnt++,ans+=e[i].w;
}
//cout<<cnt<<endl;
if( cnt==n-1 ) cout<<ans;
else cout<<"orz";
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>e[i].u>>e[i].v>>e[i].w;
}
k();
}
Prim
Kruskal是贪心取边,而Prim就是贪心取点。
算法思路
- 对于一棵树,我们的所有节点肯定都是会取的(如果不能那肯定orz)
- 每取一个点,我们就记录该点已经被取了,并且将链接该点的边纳入优先队列中
算法流程
- 建图、连边
- 将生成树的根节点所连边(任意点,一般纳入1号节点)纳入优先队列中
- 跑边,每到一个未访问过的节点,就将点数纳入生成树,边权对答案有贡献,答案增加
Code
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+12,M=4e5+12;
int n,m,u,v,w,cnt,tot,use[N],head[N],ans;
struct Edge{
int nex,to,w;
bool operator < ( const Edge &other ) const
{
return other.w<w;
}
}edge[M];
void addedge(int u,int v,int w)
{
cnt++;
edge[cnt]={ head[u],v,w };
head[u]=cnt;
}
priority_queue<Edge>q;
void prim(){
for(int i=head[1];i;i=edge[i].nex)
{
q.push(edge[i]);
use[1]=1;
tot=1;
}
while( !q.empty() )
{
Edge l=q.top();
q.pop();
int y=l.to,w=l.w;
if( use[y] ) continue;
ans+=w;
tot++;
use[y]=1;
for(int i=head[y];i;i=edge[i].nex)
{
q.push(edge[i]);
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>u>>v>>w;
addedge(u,v,w);
addedge(v,u,w);
}
prim();
if( tot==n ) cout<<ans;
else cout<<"orz";
}