部分内容摘自 李煜东《算法进阶》(个人认为非常好的一本书)
一些有关最小生成树的知识
个人比较蒻,讲得可能不太清楚,建议大家还是翻翻书,查查资料,找一些比较权威的说法。那么接下来就是蒟蒻自己的一些理解了
最小生成树,就是给你一个无向图,让你选一些边出来,然后让这些边形成的新的图保持连通且边权之和最小。这样来理解可能比较通俗易懂,其实这么看来,让一个图保持连通且权值最小,先不考虑权值,我们最少需要n-1条边让这个图保持连通,此时这就是一个树形结构(可能大概这就是为什么叫最小生成树的原因吧),但是注意最小生成树不能直接跑最短路
那么如何构造出这一颗最小生成树呢?涉及到两种算法,一个是Prim算法和Kruskal算法
Kruskal算法
时间复杂度为O(m log m)
Kruskal算法总是会去维护无向图中的最小生成森林。最初,森林由零条边构成,每个节点自己构成一棵树
在生成最小生成树的过程中,会在剩余的边中选一条边权最小的边加入到森林中,且保证这条边连接的两个点不属于同一集合(还没有被放在森林中)。这样的话,我们就可以用并查集来实现这一个过程,不会的并查集的可以去看一下我另一个博客(无耻宣传)
Kruskal算法详细流程:
1.并查集初始化,每一个节点自己构成一个集合
2.将所有边按边权从小到大排序,遍历所有边(x,y,z)
3.若x,y不属于同一集合,那么加入这一条边,并将x,y合并;否则continue
4.所有的边扫描完之后,最小生成树已经构成
struct edge{
int x,y,z;
}e[MAXN];
bool cmp(edge q,edge w){
return q.z
Prim算法
时间复杂度为O(n^2),堆优化之后为O(m log n),但其实堆优化之后不如Kruskal方便,所以Prim在处理稠密图和完全图时更加优秀,但是Kruskal还是比较实用的
Prim算法总是维护最小生成树的一部分,最初,Prim算法只确定1号属于最小生成树。我们设已经确定属于最小生成树的节点集合为T,剩余的节点集合为S。我们找到两个端点x和y,分别属于T和S,且边权最小,将y从S中删去,并加入到集合T中
我们可以维护一个数组d:对于x ∈ S,d[x]表示节点x与集合T中的节点之间权值最小的边的权值。若x ∈ T,则d[x]就等于x被加入T时选出的最小边的权值
int a[MAXN][MAXN],d[MAXN],n,m,ans;
bool v[MAXN];
void prim(){
memset(d,0x3f,sizeof d);
memset(v,0,sizeof v);
d[1]=0; //将1加入进去
for(register int i=1;i
以上大概就是对两种算法的讲解了,\(Prim\)讲解得不太清楚,可能需要读者自己理解一下(是我太菜。。。)
P2230 繁忙的都市
模板题,自己做,完全没有思维难度
P2504 聪明的猴子
我们之后的省选是这种难度那多好。
这道题稍微那么不模板一点,为什么呢?因为它有x轴和y轴,所以我们的边数应该是\(n^2\),所以加一些处理就好了,还是把代码放出来
#include
using namespace std;
double maxn=-20050206.0;
int n,m,ans,tot,now,x[2000010],y[2000010],a[2000010],fa[2000010],flag[2000010];
struct node {
int x,y;
double v;
} e[2000010];
inline bool cmp(node xx,node yy) {
return xx.v=maxn) ans++;
}
printf("%d",ans);
return 0;
}
P2212 Watering the Fields S
上面一道题的一点点变种,只是对于不符合条件的边直接丢旁边不管就行了
#include
using namespace std;
const int MAXN=2e7+50;
int n,c;
int x[MAXN],y[MAXN];
struct node {
int x,y;
int z;
} e[MAXN];
bool cmp(node q,node w) {
return q.z
P1194 买礼物
这个题很舒服啊,它是一个矩阵,所以它是一个对称的图形,我们的读入只需要读取其中的一半,而剩下的一半再读入就会出现重边的情况。再看这道题的话,记得比较优惠和原价谁更便宜(为什么优惠之后会更贵啊)
#include
using namespace std;
int n,m,u,tot,now,ans,fa[500010],flag[500010];
struct node {
int x,y,v;
} e[500010];
inline bool cmp(node xx,node yy) {
return xx.vi) {
e[++tot].x=i;
e[tot].y=j;
e[tot].v=u;
}
}
}
sort(e+1,e+1+tot,cmp);
for(register int i=1;i<=tot;i++) {
if(now==m-1) break;
int b=find(e[i].x);
int c=find(e[i].y);
if(b!=c) {
fa[b]=c;
now++;
ans+=min(e[i].v,n);
}
}
printf("%d",ans+n);
return 0;
}