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

工作证:

posted @ 2017-11-09 11:29  昵称不能为空voidf  阅读(72)  评论(0)    收藏  举报