引言
看着一章的标题,很明显和树有关,所以我们就得先知道什么是树。
树
百度百科里说树是无环的连通图。树也是任意两个结点间有且只有一条路径的图。概而言之,树首先是一个图,其次当中没有环,就这么简单。你想象一下大树的照片(除了一些畸形的枝条融合了)就和“树”很像。
最小生成树
我们再来说一下最小生成树。给你一个图,让你去除一些边后,成为一个树,并且边权和最小。
例子
原图
生成的树
应用
我们在城市之间修公路,显然,修成一个环就会很费钱,怎么修能即联通所有城市,又使总花费最小?这时候就能用到最小生成树了
prim
概述
prim算法很像Dijkstra算法,还不会Dijkstra的朋友可以看一下我的上一篇文章。
因为生成的树中,每个点都包含其中,所以我们从任意一个点开始搜索(推荐用1号点,当然,你要是一身反骨非得用别的点我也没意见),找到最短边,把最短边的权累加进答案里,标记这个点,修改dist数组
例题
题目描述
样例输入
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5050,M=2e5+100;
struct node{
int next,v,w;
}e[M];
int head[M],cnt=0;
void add(int u,int v,int w){
e[++cnt]={head[u],v,w};
head[u]=cnt;
}
struct lis{
int u,d;
};
bool operator<(const lis &x,const lis &y){
return x.d>y.d;
}
priority_queue<lis> q;
int dis[N],vis[N];
int res=0,tot=0;
void Prim(int n){
memset(dis,0x3f,sizeof(dis));
dis[1]=0;
q.push({1,0});
while(!q.empty() ){
if(tot>=n)break;
int u=q.top().u,d=q.top().d;
q.pop() ;
if(vis[u])continue;
vis[u]=1;
++tot;
res+=d;
for(int i=head[u];i;i=e[i].next){
int v=e[i].v,w=e[i].w;
if(w<dis[v]){
dis[v]=w;
q.push({v,w});
}
}
}
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
Prim(n);
cout<<res<<endl;
return 0;
}
Kruskal
由prim的算法流程易知,他是不断地选择并加入点,因而也被称为“加点法”
概述
Kruskal按边权从小到大排序,每次取出最短的边,用并查集判断边的两个端点是否连通。不连通就加进并查集,更新答案。
例题
题目描述
样例输入
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+100;
struct node{
int u,v,w;
}e[N];
bool cmp(node a,node b){
return a.w<b.w;
}
int fa[N];
void init(int n){
for(int i=1;i<=n;i++){
fa[i]=i;
}
}
int find(int x){
if(x==fa[x])return x;
return fa[x]=find(fa[x]);;
}
void add(int x,int y){
x=find(x);
y=find(y);
fa[y]=x;
}
int main()
{
int n,m;
cin>>n>>m;
init(n);
for(int i=1;i<=m;i++){
cin>>e[i].u>>e[i].v>>e[i].w;
}
sort(e+1,e+1+m,cmp);
int cnt=0;
int ans=0;
for(int i=1;i<=m;i++){
int u=e[i].u,v=e[i].v,w=e[i].w;
if(find(u)!=find(v)){
ans+=w;
add(u,v);
cnt++;
}
if(cnt==n-1){
break;
}
}
cout<<ans<<endl;
return 0;
}
这是我的第二篇文章,如有纰漏也请各位大佬指正
辛苦创作不易,还望看官点赞收藏打赏,后续还会更新新的内容。