今天主要介绍一些最小生成树问题(正边负边权都可以),最小生成树也是一种很重要的数据结构,主要处理最少花费或者最短长度问题; 最小生成树针对无向图
最小生成树算法的主要脉络:
下面首先我来介绍kruskal算法:(相比prim更加常用 本人蒟蒻)
Kruskal算法基本思想
1) 克鲁斯卡尔 (Kruskal) 算法,是用来求加权连通图的最小生成树的算法 。
2) 基本思想 :按照权值从小到大的顺序选择 n-1 条边,并保证这 n-1 条边不构成回路
3) 具体做法 :首先构造一个只含 n 个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止
原文链接:https://blog.csdn.net/weixin_45829957/article/details/108001882
Kruskal算法具体做法
- 如何处理从小到大并存储边?A:直接调用STL的sort函数,用结构体去存储边和权值
- 如何保证已经存到了n-1条边并且不构成回路?A:在模拟结束后进行特判;使用并查集,将祖宗节点不相同的点进行入队操作
例题: 洛谷P3366
#include <iostream>//不用万能头是因为本蒟蒻记不住 #include<algorithm> using namespace std; int n,m; const int INF=0x3f3f3f3f; const int N=1e5+10; const int M=4e5+10; int fa[N],index;//存储祖宗节点 struct Edge { int e,ne,w; bool operator< (const Edge &tmp) const { return w<tmp.w; } }edge[M]; int cnt,ans; int find(int x) { if(fa[x]!=x)fa[x]=find(fa[x]); return fa[x]; } //在结构体里可以直接进行重载小于号,也可以自己手写比较函数 //bool cmp(Edge a,Edge b) //{ // return a.w<b.w; //} int Kruskal() { sort(edge+1,edge+1+m);//进行排序 for(int i=1;i<=n;i++)fa[i]=i;//初始化并查集 for(int i=1;i<=m;i++) { int a,b,c; a=edge[i].e,b=edge[i].ne,c=edge[i].w; int nx=find(a); int ny=find(b); if(nx!=ny) { ans+=c; cnt++; //进行合并操作 fa[nx]=ny; } } if(cnt<n-1)return INF; else return ans; } int main() { cin>>n>>m; for(int i=1;i<=m;i++) { int a,b,c; cin>>a>>b>>c; edge[i]={a,b,c}; } int t=Kruskal(); if(t==INF)cout<<"orz"<<endl; else cout<<t<<endl; return 0; }
接下来介绍一下朴素prim如何写最小生成树的问题
Prim算法基本思想(普利姆算法)
与Dijkstra算法(求最短路)类似,Prim算法主要通过贪心策略从某个点每一次找到边权最小的边,并将这条边的另一个顶点加入最小生成树点集之中,重复n次,直到点集中加入所有顶点
#include <iostream>//不用万能头是因为本蒟蒻记不住
#include<algorithm>
#include<cstring>
using namespace std;
int n,m;
const int N = 510, INF = 0x3f3f3f3f;
int dis[N],vis[N],g[1010][1010];
int prim()
{
memset(dis,0x3f,sizeof dis);//进行一个初始化
int ans=0;
dis[1]=0;
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
{
if(!vis[j]&&(t==-1||dis[t]>dis[j]))
t=j;
}
if(i&&dis[t]==INF)return INF;//当前找到的最短距离为INF说明图不连通无最小生成树
ans+=dis[t];
vis[t]=true;
for(int j=1;j<=n;j++)//进行距离的更新
{
dis[j]=min(dis[j],g[t][j]);
}
}
return ans;
}
int main()
{
cin>>n>>m;
memset(g,0x3f,sizeof g);
for(int i=1;i<=m;i++)
{
int a,b,c;
cin>>a>>b>>c;
g[a][b]=g[b][a]=min(c,g[a][b]);
}
int t=prim();
if(t==INF)cout<<"impossible"<<endl;
else
cout<<t<<endl;
return 0;
}
谢谢浏览❤