kruskal不使用邻接矩阵的模板
kruskal算法,用来求最小生成树,适用于边数远远多于点数的图。
其核心是对边排序,然后每次选择剩下的边中权值最小的边,判断如果这条边的两个点是否已经连通,
连通了那就没必要选这条边,我们可以跳过。
没有连通,那就选择这条边,然后把两点“连”起来。
那么怎么连接这些点,使得我们可以判断这条边选还是不选呢?
那就是用并查集。
并查集+邻接表最高です!
贴一张模板题AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define M 19260817
#define MN 19890604
#define INF 2147483647
struct edge//用来存边
{
int from;//从哪来
int to;//到哪去
int wei;//这条路有多长(权值)
}E[M];
int father[MN];//并查集用,父亲数组存的是这个点所在的集合
int rank[MN];//秩,貌似用来优化并查集的,可选用,非必要
int findset(int x)//查询这个点在哪个集合里面
{
if(x!=father[x])//如果这个点在集合里面(父亲节点不是他自己)
father[x]=findset(father[x]);//就去找他的父亲节点,并直接让他指向他的父亲
return father[x];//如果这个点不在集合里,那他父亲节点就是他自己
}
bool checkset(int x,int y)//检查两个点是否已经连通,即检查他们是否属于一个集合,即检查他们父亲节点是否一致
{
x=findset(x);//找到他们父亲节点
y=findset(y);
if(x==y)//一致的话返回1,用于判断
return 1;
else
return 0;
}
void uni(int x,int y)//连接两个点用
{
int fx=findset(x);//其实完全没必要新定义两个变量存他们父亲
int fy=findset(y);//我这里是debug的时候留下来的东西
if(rank[fx]<rank[fy])//如果y的爹比x的爹高级,那x的爹就认y的爹叫爹
father[fx]=fy;
if(rank[fx]>=rank[fy])//反之y认x爹
{
father[fy]=father[fx];
if(rank[fx]==rank[fy])rank[fx]++;//爹同级怎么办?我们让x爹自升一级
}//怎么感觉有点欺负人
return;
}
inline int re()//快读,不好意思用上瘾了
{
int x=0;
int w=1;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')
w=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
x=(x<<3)+(x<<1)+(c^48);
c=getchar();
}
return x;
}
bool mmp(edge a,edge b){return a.wei<b.wei;}//这是排边用比较函数,里面的return值表示升序
int minsum=0;//存最小生成树所有边的权值
bool kruskal(int ap,int m)//ap是所有点的个数,m是边数
{
int i;//循环用
int ctr=1;//计算目前有多少连边,用于判断图是否连通
for(i=1;i<=m;i++)//跑一遍排好序的边
{
if(checkset(E[i].from,E[i].to))continue;//如果那条边所对应两个点已经连通,那就跳过这条边
else
{
uni(E[i].from,E[i].to);//否则把他们连上
minsum+=E[i].wei;//总权值要增加,因为你选了这条边
ctr++;//又多连通一个点
}
}
if(ctr<ap)//如果最后连通的点没有一开始那么多
return 0;//那我们的图没有连通
else
return 1;
}
int main()
{
register int i,n,m;//n是节点数量,m是边数
n=re();m=re();
for(i=1;i<=m;i++)//把每条边的信息读入(从哪来,到哪去,路有多长)
{
E[i].from=re();
E[i].to=re();
E[i].wei=re();
}
std::sort(E+1,E+m+1,mmp);//对边的权值进行排序
for(i=1;i<=n;i++)father[i]=i;//初始化并查集,大家都没连通,那就有多少个点多少个集合咯,大家都认自己叫爹
if(!kruskal(n,m))//如果不连得通
printf("orz");//这个题目叫你们打什么你们就打什么
else
printf("%d",minsum);//连通就打出最小权值和,有些题目也不是问的这个值
return 0;
}
//搬运或自用随意,有问题留言,喜欢的话给个赞?ありがとう
//对应题目:https://www.luogu.org/problemnew/show/P3366
工作证:

浙公网安备 33010602011771号