023(【模板】最小生成树)(最小生成树)
作者:互联网
题目:https://www.luogu.com.cn/problem/P3366
题目思路:题目名字就已经说清楚了“最小生成树”
首先,把点(程序中为 m)和边(程序中为 n)输入进去
而后一个 for 循环把边以及这个边的两个端点输入进去
那么该如何存储呢?
首当其冲地,有人想到了用三个一维数组,用一一对应的方式解决
如果依照这个走下去,到后面在使用 kruskal 算法是需要对这些边依照从小到大的顺序进行排列的
可问题就在你把人家存边的数组排了,对应关系不也就不存在了吗?
所以这个“三一维”的方式不可取
所以,此时结构体就能派上用场,排序也只需要多拿出来三行写个 cmp
bool cmp(jardin a,jardin b){//jardin是结构体的名字
return a.di<b.di;//di是边的权值
}
再把它扔进 kruskal 的函数里,把 ans 加出来,再在主程序里输出
自此,主程序的主体部分成型,到后面出了问题再改就可以
向前翻,跑到核心程序区“kruskal”
首先得想明白,kruskal 是如何运行的
所有的顶点在没有链接之时,都是一个子集,每个顶点单独作为一个子集
而后在所有边中找到一条最小的边,由“MST性质”(详见博客022)可得,这条最小的边如果将两个互不相通的子集联系起来,那么最终的最小生成树肯定有这条边,那么就记录在案,ans+=
BUT! 来做个小实验
把 n 个点拼成一个连通图,最多要多少条边
你会发现你根本就不用把它搞成一个闭环
最多也就是像这样用 n-1 个边凑成一个只开了一个口的闭环
但是,你如果想删除其中一个边,那它会又多出来一个缺口,一共两个缺口把它分成了两个互不相通的子集
你还得要一条边把这俩货连起来,得了,又是 n-1
如法炮制,我们得到了一个普遍规律:“把 n 个点拼在一起,一定要 n-1 条边,且只要 n-1 条边”
这一句话对于顶点集的一个子集也适用
换句话说,最小生成树里不可能存在闭环
如果连起来之后成了一个闭环,或者说在本来就已经连通的一个子集里又要连一条边
那就可以跳过了,这个边并不需要存在
针对这个现象,“并查集”的算法就可以被牵出来溜溜了
首先,来一个F数组,循环令 F[i]=i,是由于上文“所有的顶点在没有链接之时,都是一个子集,每个顶点单独作为一个子集”
如果确定某条边被要了,除了 ans+= 外,还要让 F[边起点]=边终点,表示他们已经合并成了一个子集
再写一个函数,从边的两个点往他们的终点一直跑,什么时候 F[x]=x,也就是没路了的时候,视为结束
如果两个点结束的地方一样,那就证明“他们已经在一个集里”,此时跳过此边
等到什么时候连的边的数量达到了 n-1,就意味着已经成为最小生成树,结束,回到主程序把 ans 打出来
如果到死也没有 n-1,那么给个信号,输出 orz
至此
#include<bits/stdc++.h>
using namespace std;
int m,n,ans=0,f[5001];//f为集
int x,y,d;
struct jardin{
int s,e,di;
}q[200001];//结构体存起点,终点与权值
bool cmp(jardin a,jardin b){//排序
return a.di<b.di;
}
int find(int t){//对集进行查找
if(f[t]==t){
return t;
}
f[t]=find(f[t]);
return f[t];
//死命找祖先
}
void kruskal(){
int f1,f2,side=0;//side计数
for(int i=1;i<=m;++i){//最开始每个点一个集
f[i]=i;
}
for(int i=1;i<=n;++i){
f1=find(q[i].s);
f2=find(q[i].e);
//两个点分别找终点
if(f1==f2){//找到的终点一样
continue;
//都成一个集了,continue
}
ans+=q[i].di;//加数
f[f1]=f2;//合并
if(++side==m-1){//如果排出了(点-1)
break;//再见
}
}
if(side!=m-1){//如果到死也没有
ans=0;//给信号
}
}
int main(){
scanf("%d%d",&m,&n);
for(int i=1;i<=n;++i){
scanf("%d%d%d",&q[i].s,&q[i].e,&q[i].di);
}
sort(q+1,q+1+n,cmp);//排序
kruskal();
if(ans!=0){//如果不为零输出ans
printf("%d",ans);
}
else{//如果为零,证明上面给了信号,拼不上
printf("orz");
}
return 0;
}
标签:一个,最小,生成,子集,023,ans,条边,jardin 来源: https://www.cnblogs.com/a-001/p/16439454.html