38.最小生成树
1. Kruskal 算法
算法步骤:
-
把图中所有边从小到大排序
for(int i=1;i<=m;i++) { cin>>e[i].in>>e[i].next>>e[i].w; } sort(e+1,e+m+1,cmp);
-
然后从小到达依次选择边
-
如果选择的边的两个端点在不同的树上,则增加该边(利用Union-find set 实现)
bool cmp(const edge &a,const edge &b) { return a.w<b.w; } void initf() { for(int i=1;i<=n;i++) h[i]=i; } int find(int a) { return (h[a]==a?h[a]:(h[a]=find(h[a]))); } bool mergef(edge a) { int pa = find(a.in),pb=find(a.next); if(pa!=pb) {h[pb] = pa;ans+=a.w;return true;} return false; }
-
最终若已经增加了n-1一条边(若有n给结点)则结束循环2,3
for(int i=1,j=0;j<n&&i<=m;i++) { if(mergef(e[i])) j++; }
完整程序如下:
#include<bits/stdc++.h> using namespace std; const int maxe = 200005; typedef struct { int in; int w; int next; }edge; edge e[maxe]; int h[5010],n,m; int ans=0; bool cmp(const edge &a,const edge &b) { return a.w<b.w; } void initf() { for(int i=1;i<=n;i++) h[i]=i; } int find(int a) { return (h[a]==a?h[a]:(h[a]=find(h[a]))); } bool mergef(edge a) { int pa = find(a.in),pb=find(a.next); if(pa!=pb) {h[pb] = pa;ans+=a.w;return true;} return false; } void funcf() { cin>>n>>m; for(int i=1;i<=m;i++) { cin>>e[i].in>>e[i].next>>e[i].w; } sort(e+1,e+m+1,cmp); initf(); for(int i=1,j=0;j<n&&i<=m;i++) { if(mergef(e[i])) j++; } } int main() { funcf(); cout<<ans; return 0; }
2. Prim 算法
算法步骤:
-
从任意一个点开始,将其加入到 V 1 V_1 V1中。
-
选择 V 1 V_1 V1和 V 2 V_2 V2的最短横切边对应的点加入到 V 1 V_1 V1(切分定理 [ 1 ] ^{[1]} [1])
-
重复步骤2直到加入n个结点结束
-
本程序采用priority_queue优先队列进行优化,代码如下:
#include<bits/stdc++.h> using namespace std; const int maxe = 400005; const int inf = 9999999; typedef struct { int in; int w = inf; int next; }edge; typedef struct node { int h,w; bool operator<(const node &a) const { return w>a.w; } }node; edge e[maxe]; int h[5010],n,m,ans,dis[5010]; bool vis[5010]; void inputf() { int x,y,z; cin>>n>>m; for(int i=1;i<=m;i++) { cin>>x>>y>>z; e[i].in=y; e[i].w=z; e[i].next=h[x]; h[x]=i; swap(x,y); int j = i+m; e[j].in=y; e[j].w=z; e[j].next=h[x]; h[x]=j; } } inline void initf() { for(int i=0;i<=5000;i++) { dis[i] = inf; } } void funcf() { int k=0; initf(); priority_queue<node> Q; Q.push(node{1,0}); while(k<n) { node s = Q.top(); Q.pop(); if(vis[s.h]) continue; vis[s.h]=true; k++; ans+=s.w; for(int i=h[s.h];i!=0;i=e[i].next) { if(!vis[e[i].in]) if(e[i].w<dis[e[i].in]) { dis[e[i].in]=e[i].w; Q.push(node{e[i].in,e[i].w}); } } } } int main() { inputf(); funcf(); cout<<ans; return 0; }
3.模板题
[ 1 ] ^{[1]} [1]切分定理:
定义1:把图中结点分为两部分,称为一个切分。
定义2:如果一个边的两个端点,属于切分不同的两边,则称为横切边
如果任意切分,横切边权值最小的边必然属于最小生成树