kruskal/Prim算法求最小生成树
传送门:P3366[模板]最小生成树
最小生成树的定义
一个有 n n n个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n n n个结点,并且有保持图连通的最少的边(即 n − 1 n-1 n−1条边),并且这 n − 1 n-1 n−1条边的权值之和是所有生成树中最小的极小连通子图,那么这个连通子图就叫做最小生成树。
kruskal算法的原理说明
运用贪心思想,按边权的大小取边依次用并查集合并,如果加入边数
=
n
−
1
=n-1
=n−1,那么就说明找到了最小生成树。若设整张图的边的个数为
m
m
m,则
k
r
u
s
k
a
l
kruskal
kruskal算法的时间复杂度为
O
(
m
log
m
)
O(m\log m)
O(mlogm)
参考代码
#include<bits/stdc++.h>
#define Maxn 200010
using namespace std;
struct node{
int u,v,c;
}edge[Maxn];
int n,m,father[5001];
inline int read(){
int x=0;char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0' and c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return x;
}
inline bool cmp(node a,node b){
return a.c<b.c;
}
inline int getfather(int x){
if(father[x]==x)return x;
father[x]=getfather(father[x]);
return father[x];
}
inline void combine(int i,int j){
i=getfather(i);
j=getfather(j);
if(i!=j)father[i]=j;
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++)
edge[i].u=read(),edge[i].v=read(),edge[i].c=read();
for(int i=1;i<=n;i++)
father[i]=i;
sort(edge+1,edge+m+1,cmp);
int tmp=0,ans=0;
bool check=false;
for(int i=1;i<=m;i++){
int a=edge[i].u,b=edge[i].v;
if(getfather(a)!=getfather(b)){
combine(a,b);
tmp++;
ans+=edge[i].c;
}
else continue;
if(tmp==n-1){
check=true;
break;
}
}
if(check)
cout<<ans<<endl;
else
cout<<"orz"<<endl;
return 0;
}
Prim算法
P
r
i
m
Prim
Prim算法适合稠密图,即边数较多而点较少的情况,时间复杂度为
O
(
n
2
)
O(n^2)
O(n2),堆优化的情况下,如果点数为
m
m
m,边数为
n
n
n,可以达到
O
(
n
log
m
)
O(n\log m)
O(nlogm)。
这个算法的思想很简单,就是每次寻找一条由已加入集合的点和与它们相邻的没加入集合的点的权值最小边,进行
n
−
1
n-1
n−1次就找出来了,也是贪心思想,实现就是随便找一个初始节点,然后建一个最小堆(边小的先
p
o
p
pop
pop出来),把该节点的
v
i
s
vis
vis值置为
1
1
1,遍历该节点相邻的节点,如果没有被
v
i
s
vis
vis标记过,就加入边到堆中,扫完了以后处理堆中数据,如果弹出的边被标记过就
p
o
p
pop
pop,没有就取出来,把边通往的节点置为
k
e
y
key
key,下次就加入
k
e
y
key
key节点有关没有标记过的边。一直循环,由于最小生成树边数与节点的关系为
m
=
n
−
1
m=n-1
m=n−1,所以要循环
n
−
1
n-1
n−1次,把每一次堆弹出边的值累加起来就是最小生成树的值了
参考代码
#include<bits/stdc++.h>
#define in read()
#define re register
#define pii pair<int,int>
#define mp make_pair
#define sd second
#define fl first
#define MAXN 200050
#define MAXM MAXN<<1
#define N 5005
using namespace std;
int dis[N],n,m,cnt=0,ans=0;
bool vis[N];
int nex[MAXM],first[MAXN],to[MAXM],val[MAXM],tot=0;
priority_queue<pii,vector<pii>,greater<pii> >q;
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return x*f;
}
inline void addedge(int u,int v,int w){
nex[++tot]=first[u];
first[u]=tot;
to[tot]=v;val[tot]=w;
}
int main(){
n=in,m=in;
for(re int i=1;i<=m;i++){
int u=in,v=in,w=in;
addedge(u,v,w);
addedge(v,u,w);
}
memset(dis,0x3f3f3f3f,sizeof dis);
q.push(mp(dis[1],1));dis[1]=0;//入堆第一个点
while(!q.empty() and cnt<n){
int u=q.top().sd;q.pop();
if(vis[u])continue;
vis[u]=true;cnt++;ans+=dis[u];
for(int e=first[u];e;e=nex[e]){
int v=to[e];
if(!vis[v] and dis[v]>val[e]){
dis[v]=val[e];//先更改v
q.push(mp(dis[v],v));//然后入堆
}
}
}
for(int i=1;i<=n;i++)
if(!vis[i]){
cout<<"orz"<<'\n';
return 0;
}//判断是否连通
cout<<ans<<'\n';
return 0;
}