0.前置知识
存图方式(邻接表,邻接矩阵),并查集(嘚瑟一下,我自己专门写了一篇)。
不会的快进入链接学习吧!
1.引入
生成树,就是从一个图中选中
n
−
1
n-1
n−1条边,使得这些边构成一棵树,并包含图中的所有节点。
最小生成树,就是找到一种生成树,使得这个生成树的边权和最小。
2.生成方式一:prim
这种方法有点类似Dijstra,就是每次从所有 v i s vis vis过的点遍历能达到的边,从其中选择一条最小的,加入生成树。
找到从
0
0
0出发的最小的边:
[
0
,
2
]
[0,2]
[0,2],边权为
3
3
3,那么对
2
2
2号点进行标记。
然后从
0
0
0号和
2
2
2号节点继续找,发现最小的是
[
2
,
4
]
[2,4]
[2,4]边,那么就标记
4
4
4号节点。
然后是
[
4
,
1
]
[4,1]
[4,1],
1
1
1号。
以此类推,最后就生成出来了这样一个图:
这就是prim算法 ,代码我不会写在后面。
3.生成方式二:kruskal
这个算法本质上就是把所有边按照边权排序,然后直接爆炸按顺序判断要不要加进生成树里。
kruskal算法使用了一种极速闪电致命又自杀的东西:并查集。比
l
o
g
n
log n
logn还要快,是
∗
l
o
g
n
*log n
∗logn。
他有多快呢?
n n n | 他的复杂度 |
---|---|
( 1 , 2 ] (1,2] (1,2] | 1 1 1 |
( 2 , 4 ] (2,4] (2,4] | 2 2 2 |
( 4 , 16 ] (4,16] (4,16] | 3 3 3 |
( 16 , 65536 ] (16,65536] (16,65536] | 4 4 4 |
( 65536 , 2 65536 ] (65536,2^{65536}] (65536,265536] | 5 5 5 |
所以看到了吗,n是
2
65536
2^{65536}
265536的时候复杂度还只有5。
2
65536
2^{65536}
265536有多大,我把他拷进来的时候整个电脑卡死了。他有19729位。想通了吧?
详细讲解还是看看这个吧(又嘚瑟)
好了我们在建一个图模拟一下吧 (其实还是刚才那个图,搞了搞边权)
太好了,我们先给边排序。最小的是 [ 2 , 3 ] [2,3] [2,3],把他揪出来,判断一下。怎么判断呢?首先拿并查集看一看,这个边连接的两个点在不在同一个集合内,不在的话就把这条边加入生成树,然后把两边都合并到一个集合里。否则的话就直接忽略。这一条边符合要求,加进并查集里,现在{ 2 , 3 2,3 2,3}是一个集合,剩下都是独立的。
现在最小的有
[
1
,
4
]
[1,4]
[1,4]和
[
4
,
5
]
[4,5]
[4,5],我们都判断一下,都可以。
然后就是
[
0
,
4
]
[0,4]
[0,4]和
[
2
,
4
]
[2,4]
[2,4],依然都是可以的。
这样,一颗活灵活现的生成树就出现了。
好了!
4.代码(luoguP3366)
kruskal的代码又短又易于理解,甚至可以直接用数组存边,所以他非常好写,推荐。
prim
#include "iostream"
#include "queue"
#include "cstring"
using namespace std;
#define maxm 5000010
#define maxn 1000010
#define inf 0x3f3f3f3f
int ans=0;
struct node{
int v,next,c;
}e[maxm<<1];
int h[maxn],tot,n,m;
void adde(int u,int v,int c){
tot++;
e[tot].v=v;
e[tot].c=c;
e[tot].next=h[u];
h[u]=tot;
}
int dis[maxn];
bool vis[maxn];
struct qu{
int id,dis;
bool operator < (const qu t)const{
return dis>t.dis;
}
};
priority_queue<qu> q;
void dijk(int s){
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
while(!q.empty())q.pop();
qu t;
t.dis=0;
t.id=s;
dis[s]=0;
q.push(t);
while(!q.empty()){
t=q.top();q.pop();
if(vis[t.id])continue;
vis[t.id]=true;
ans+=t.dis;
for(int j=h[t.id];j;j=e[j].next){
int v=e[j].v;
if(!vis[v]&&dis[v]>e[j].c){
dis[v]=e[j].c;
qu now;
now.id=v;
now.dis=dis[v];
q.push(now);
}
}
}
}
int main(){
int s=1;
cin>>n>>m;
int u,v,c;
for(int i=1;i<=m;i++){
cin>>u>>v>>c;
adde(u,v,c);
adde(v,u,c);
}
dijk(s);
cout<<ans<<endl;
return 0;
}
kruskal
#include "iostream"
#include "algorithm"
using namespace std;
int n,m,tot,sum,f[5001];
struct node{
int u,v,c;
}a[200010];
int cmp(const node &u,const node &v){
return u.c<v.c;
}
int getf(int u){
if(f[u]==u)return u;
return f[u]=getf(f[u]);
}
int main(){
cin>>n>>m;
if(m<n-1){cout<<"orz"<<endl;return 0;}
for(int i=1;i<=m;i++)cin>>a[i].u>>a[i].v>>a[i].c;
for(int i=1;i<=n;i++)f[i]=i;
sort(a+1,a+m+1,cmp);
for(int i=1;i<=m;i++){
int fu=getf(a[i].u),fv=getf(a[i].v);
if(fu!=fv){
f[fu]=fv;
sum++;
tot+=a[i].c;
}
if(sum==n-1)break;
}
cout<<tot<<endl;
}
萌新刚学,码风请喷