最小生成树
有n个城市,给出了m条边,从m条边里面选n-1条边使得n个城市连通,且要求花费最小
样例输入:
6 9 (6个城市,9条边)
2 4 11
3 5 13
4 6 3
5 6 4
2 3 6
4 5 7
1 2 1
3 4 9
1 3 2
输出样例:
19
Kruskal算法:
适用于稀疏图
思想:将所有的边从小到大排序,再遍历所有边,如果该边的两个点不在同一个集合,那么将该边加入最小生成树,直到加入n-1条边
#include <stdio.h> #include <iostream> #include <algorithm> #include <vector> using namespace std; bool vis[105]; long long a[1005]; int n,m,c=0; long long ans; struct edge{ int u,v,w; }; edge e[105]; bool cmp(edge e1,edge e2){ //边的排序 return e1.w<e2.w; } int getf(int v){ //找祖先节点 if(a[v]==v){ return v; } else{ a[v]=getf(a[v]); return a[v]; } } int merge(int u,int v){ //判断是否将边加入最小生成树 int t1=getf(u); int t2=getf(v); if(t1!=t2){ //边的两个点不在同一节点,则加入最小生成树 a[t2]=t1; return 1; } else{ //否则不加入 return 0; } } int main() { cin>>n>>m; for(int i=1;i<=n;i++){ //初始化并查集数组 a[i]=i; } for(int i=0;i<m;i++){ //输入 cin>>e[i].u>>e[i].v>>e[i].w; } sort(e,e+m,cmp); //从小到大排序 for(int i=0;i<m;i++){ if(merge(e[i].u,e[i].v)){ //判断边是否需要加入 加入就节点数+1 c++; ans+=e[i].w; } if(c==n-1){ //只需要n-1条边即可 break; } } cout<<ans; return 0; }
prim算法:
#include <stdio.h> #include <iostream> #include <algorithm> #include <vector> using namespace std; const int inf=999999; bool vis[105]; int dis[105], a[1005]; int n,m,c=0; int ans; struct edge{ int v,w; edge(int xx,int yy){ v=xx;w=yy; } }; vector<edge> v[105]; void prim(int s){ int mi,pmi; for(int i=1;i<=n;i++){ //初始化dis数组 使每条边都不相连 dis数组表示点到生成树的最小距离 dis[i]=inf; } dis[s]=0; //起始点 while(c<n){ // 需要找到n条边 mi=inf; for(int i=1;i<=n;i++){ if(dis[i]<mi&&!vis[i]){ //每次找dis数组里面最小距离的点pmi 设置为加入最小生成树 mi=dis[i]; pmi=i; } } vis[pmi]=1; ans+=mi; c++; //加入一个点则生成树节点+1 for(int i=0;i<v[pmi].size();i++){ // 找该次dis距离最小的点所连接的点,更新到dis数组 edge t = v[pmi][i]; if(!vis[t.v]&&t.w<dis[t.v]){ dis[t.v]=t.w; } } // for(int i=1;i<=n;i++){ // cout<<dis[i]<<" "; // } // cout<<endl; } } int main() { int s,x,y,value; cin>>n>>m>>s; for(int i=1;i<=m;i++){ cin>>x>>y>>value; v[x].push_back(edge(y,value)); v[y].push_back(edge(x,value)); } prim(s); cout<<ans; return 0; }