前言
突然想复习一波图论。。。
最小生成树:口语化的说,就是在原图中选 n − 1 n-1 n−1条边组成一棵树,使这几条边权值和最小
1.kruskal
这是一个基于边的算法。
对所有边排序,从最小的边开始枚举,用并查集维护现在选定的集合中点的联通情况,若一条边的两个端点不属于同一集合,就 m e r g e merge merge并加入该边边权
正确性显然。
代码如下 ↓ \downarrow ↓
int f[N];
struct qwq{int u,v,w;} e1[N];//输入时存储
bool cmp(qwq& x,qwq& y) {return x.w<y.w;}
void init(int n) {for(int i=1;i<=n;i++) f[i]=i;}
int find(int x) {return (f[x]==x?x:f[x]=find(f[x]));}
void merge(int x,int y) {f[find(x)]=find(y);}
void kruskal() {
init(n);
sort(e1+1,e1+m+1,cmp);
for(int i=1;i<=m;i++) {
int u=e1[i].u,v=e1[i].v,w=e1[i].w;
if(find(u)!=find(v))
merge(u,v);
}
}
复杂度 O ( m l o g m ) O(mlogm) O(mlogm)
2.prim
这是一个基于点的算法。
d [ u ] d[u] d[u]表示的是u对于现在已经选定的树的最小距离。每次找 d [ ] d[] d[]最小且未被选定的 u u u,将其加入集合,并用它更新每个点
正确性依旧显然。
代码如下 ↓ \downarrow ↓
bool vis[N];
int d[N];
void prim() {
memset(d,0x3f,sizeof(d));
d[1]=0;
for(int k=1;k<=n;k++) {
int u=0,minn=1e9;
for(int i=1;i<=n;i++)
if(vis[i]==0&&d[i]<minn)
u=i,minn=d[i];
vis[u]=1;
for(int i=h[u];i;i=e[i].nxt) {
int v=e[i].v,w=e[i].w;
if(vis[v]==0)
d[v]=min(d[v],w);
}
}
}
复杂度 O ( n 2 ) O(n^2) O(n2)
事实上和 d i j k s t r a dijkstra dijkstra一样, p r i m prim prim也有堆(优先队列)优化版,但由于 p r i m prim prim本身个人感觉就没有 k r u s k a l kruskal kruskal应用广,所以就没去写优化版。
3.总结
2 2 2种算法都是贪心的,可以根据是稀疏图还是稠密图选择合适的算法,在我做的题中好像 k r u s k a l kruskal kruskal应用广泛一点。