HDU 1856 More is better 并查集 路径压缩

做了并查集一段时间了。个人觉得利用并查集解题的套路其实很单调。

1、开一个数组记录各个节点的父节点,初始化

2、给出一对关联的数据,查找

3、根据查找结果如果根不属于同一集合则合并

当然,还可以优化。主要是在查找利用递归,使得在回溯时各个节点的父节点都是树的根节点。下次查找就可以

降低查找长度。其次,可以利用一个数组记录每棵树的高度。在合并时将矮树链接到高树上,使得新生成的树尽

量矮。

大家可以看看这两个链接:点击打开链接 和点击打开链接

 

我做这题的想法:

1、在合并两棵树时一般要求是根节点不相同才合并。其实,我觉得这个要求也是可以根据实际情况来看的。

如果只是比较单纯的并查集问题可以相同也合并,合并前后也没有啥变化。但是比如本题就必须是不同才合并。

相同也合并会对num[]造成影响。

2、注释掉的代码是用来在合并时将矮树链接到高树上用的。可是空间不够了。

大家在看代码时可能会发现这样一个问题,以a为根的树链接到以b为根的树上后,height[a]应该赋值为0。代码中

并没有这样做。因为不处理也不影响。为啥呢?因为a已经不再是根了。即使下次要处理一组关联的边(a,c)时,也

是用a的根和c链接。也就是说a不会再成为根,height[a]也不会再被用了。

 

AC代码:

#include<iostream>
using namespace std;

const int MAX=10000001;
int father[MAX];  //father[i]存放i的父节点
int num[MAX];    //num[i]以i为根的节点数
//int height[MAX];  //树的高度

void initial()   //初始化
{
	for(int i=0;i<MAX;i++)
	{
		father[i]=i;
		num[i]=1;   //每个i都是根节点,节点数都为1
		//height[i]=1;
	}
}

int find(int a)    //查找
{
	if(a==father[a])
		return a;	
	return father[a]=find(father[a]);  //路径压缩
}

void combine(int a,int b)   //不在同一集合就合并
{
	int tmpa=find(a);
	int tmpb=find(b);

	if(tmpa!=tmpb)
	{
		father[tmpa]=tmpb;
		num[tmpb]+=num[tmpa];
		num[tmpa]=0;

		/*
		if(height[tmpb]>height[tmpa])  //每次合并都使树的高度尽量小
		{
			father[tmpa]=tmpb;
			num[tmpb]+=num[tmpa];
			num[tmpa]=0;
		}
		else
		{
			father[tmpb]=tmpa;
			num[tmpa]+=num[tmpb];
			num[tmpb]=0;

			if(height[tmpa]==height[tmpb])
				height[tmpa]++;
		}
		*/
	}
}

int maxNode()   //连通分量最大节点数
{
	int tmp=0;
	for(int i=0;i<MAX;i++)
	{
		if(num[i]>tmp)
			tmp=num[i];
	}

	return tmp;
}

void preDeal(int n)   //预处理
{
	int a,b;

    for(int i=0;i<n;i++)
	{
		scanf("%d%d",&a,&b);
		combine(a,b);
	}
}

int main()
{
	int n;

	while(scanf("%d",&n)==1)
	{
		initial();
		preDeal(n);

		printf("%d\n",maxNode());
	}

	return 0;
}

你可能感兴趣的:(HDU 1856 More is better 并查集 路径压缩)