对于最小生成树,小白书上提供了prime算法与克鲁斯卡尔算法。
相比较而言,个人认为克鲁斯卡尔要优于prime,并且更加好理解一些(而且一看名字的逼格就很高,五个字的算法好像听起来都很厉害的样子。)
如果执意要用prime算法,出门右转,这篇笔记不用看了。
克鲁斯卡尔的核心思想就是以图的边为基础,没错是"边"!不要用"点"。
不过就是储存每条边的权值,然后快排一波,按照边来判断两个端点是否在同一个集合中,这里就要用到并查集的father()函数了。如果不在同一集合中,就将两个点的祖先结点连成一个树,并在总和上加上这条边的权值。
来波代码……
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 struct sd{ 6 int x,y,w; 7 }; 8 //注意用结构体来存边。 9 sd a[100005]; 10 int f[100005]; 11 bool cmp(sd a,sd b) 12 { 13 return a.w<b.w; 14 } 15 int father(int v) 16 { 17 if(f[v]!=v)f[v]=father(f[v]); 18 return f[v]; 19 } 20 //牛逼的father()函数。 21 int main() 22 { 23 int n,m=0; 24 scanf("%d",&n); 25 for(int i=1;i<=n;i++) 26 for(int j=1;j<=n;j++) 27 { 28 int x; 29 scanf("%d",&x); 30 if(x!=0) 31 { 32 m++; 33 a[m].x=i;a[m].y=j;a[m].w=x; 34 } 35 } 36 //注意!!!此种读入方法是按照邻接矩阵来读入, 37 //也可以用“端点1,端点2,边权”的方式读入 38 for(int i=1;i<=n;i++) 39 f[i]=i; 40 int tot=0; 41 sort(a+1,a+1+m,cmp);//快排一波 42 for(int i=1;i<=m;i++) 43 { 44 int fx=father(a[i].x); 45 int fy=father(a[i].y);//找祖先。 46 if(fx!=fy)//判断是否有公共祖先,即是否在同一集合中。 47 { 48 f[fx]=fy; 49 tot=tot+a[i].w;//加上权值。 50 } 51 } 52 printf("%d",tot); 53 return 0; 54 }
上面是一个模板。
下面来两道基础的例题,可供新手练习。
先来一道基础题
http://ybt.ssoier.cn:8088/problem_show.php?pid=1391
稍稍升级的一道题目
http://ybt.ssoier.cn:8088/problem_show.php?pid=1393
接着一道有趣的让我有点懵的多个最小生成树,仔细想想其实根本不难,只有小改。
https://www.luogu.org/problem/show?pid=1195
再来一波