克鲁斯卡尔算法(侧重边解决最小生成树问题)

最小生成树:

        在一个图里寻找一些边的集合,使所有点都连通,并且这些边的权重之和最小。例如要在城市之间修建地铁使所有城市都可以互相连通,城市之间的距离不同,则一定有一个方案使修建地铁的花费最小。花费最小的方案就是这个图的最小生成树。

        克鲁斯卡尔算法侧重在边的角度来解决此类问题。与之相对的还有普里姆算法,侧重在点的角度来解决此类问题。我们应根据现实要求来选择这两个算法。普里姆算法详解:https://blog.csdn.net/Q_M_X_D_D_/article/details/84869853

基本思想:

        因为最小生成树的边的权重整体是较小的,所以我们先给图中所有边按权重从小到大进行排序。然后遍历这些边,尽量选取权重小的边加入边集,若加入一条边后会使边集产生回路,则这条边是无用的,因为若产生回路,说明这两点已经是联通的了,那么再加入这条边是和不加一样,还会使权重变大,所以我们跳过这条边。在遍历过程中,若已经得到了最小生成树,那么此时所有点已经是联通的而且没有回路的。再加入别的边一定会形成回路,所以我们不用担心加入多余的边。

        这个过程中要解决的一个难点就是如何判断一条边加入后会不会形成回路,即判断两个点是否相通,不难想到,并查集。用并查集来找一个点的源点,然后判断两个点的源点是否相同,若相同则说明这两个点是联通的,若不相同则可以将这条边加入边集。

数据结构:

  • 克鲁斯卡尔算法要按边的权重排序,所以用一个结构体来储存边的信息是非常方便排序的,结构体的元素有边的起点和终点、边的权值。
  • parent数组,用来保存每个点的源点。用于判断两个点是否相通。

算法过程:

  1. 建图。将图的邻接矩阵转为结构体数组,或直接输入边的结构体数组。
  2. 对结构体数组按权值从小到大排序。
  3. 从权值最小的边开始遍历,判断边的起点和终点是否相通。若相通,则说明加入该边会产生回路,此时跳过这条边继续遍历,选择次小边。
  4. 重复3,直到所有边都遍历完。

c++代码:

#include
#include
#include
#define max 101
#define inf 0x7fffffff	//inf表示两条边不相连 
using namespace std;
struct gra 
{
	int s;
	int e;
	int v;
}edge[max];
int parent[max];
//按权值从小到大排序 
bool cmp(struct gra n1,struct gra n2)
{
	return n1.v0)
		a=parent[a];
	return a;
}
int kruscal(int m)
{
	int res=0;
	//对结构体数组排序 
	sort(edge,edge+m,cmp);
	memset(parent,0,sizeof(parent));
	for(int i=1;i<=m;i++)
	{
		//找到一条边起点和终点的源点 
		int a=find(edge[i].s);
		int b=find(edge[i].e);
		//如果源点相同则放弃这条边 
		if(a!=b)
		{
			//累加权值 
			res+=edge[i].v;
			//将两个点连起来 
			parent[b]=a;
		}
	}
	return res;
}
int main()
{
	int n,m,a,b,cost;
	cin>>n>>m;
	//克鲁斯卡尔算法对点不敏感,可以直接在输入时就将邻接矩阵转换为结构体数组 
	for(int i=1;i<=m;i++)
		cin>>edge[i].s>>edge[i].e>>edge[i].v;
	cost=kruscal(m);
	cout<

例题:ccf地铁修建

例题:HDU - 1875畅通工程再续

例题:ccf数据中心

你可能感兴趣的:(最小生成树)