您的位置:首页 > 理论基础 > 数据结构算法

POJ 1182 1703 并查集的应用

2009-09-15 14:35 288 查看
1182

今天接触了并查集,并查集在数据结构上很简单,虽说是树(二叉树都怕,更别说是树了),但是它是有根节点表示法来表示的,可以用数组模拟,相当的简单。但是用处有很大。

并查集主要用于这样的问题,问题中有大量数据,数据之间有简单的关系,而这种关系又带有简单的传递性。

如果按照矩阵的方式表示数据间的关系,即用d[i][j]表示i和j之间的关系,空间复杂度O(n^2)显然无法承受,这时就用到了并查集。

并查集的空间复杂度为O(n)。

并查集提供三个操作:

1。初始化

2。查找结点所在集合

3。合并两个集合

并查集以森林的形式存在,每个森里都代表一个集合,集合中的结点间存在某种关系,集合是由根节点进行标示的,即判断两个节点是否在一个集合里,只需看两节点所在集合的根结点是否相同即可。

理解尚浅,待多做几道并查集的题后,再来总结吧。

本题代码如下,注释很详细:

 

另外,本题cin的话会TLE,而scanf就可以AC,让我见识了scanf和cin的差距,也是第一次遇见输入量这么大的题 100000个输入

//1182
#include <iostream>

using namespace std;

struct {
int kind;
int parent;
}animal[50010];

void MakeSet(int SizeOfSet)
{	//初始化并查集,将每个结点的根结点设置为自己
//相互之间关系确定的结点才放入一个集合里
//一句话都没有输入之前所有节点之间的关系都不确定,所以各自单独一个集合
//每个结合的根节点kind都是0,有利于合并时kind值的计算
for (int i = 0;i < SizeOfSet;i++)
{
animal[i].parent = i;
animal[i].kind = 0;
}
}

void Union(int RootOfX,int RootOfY,int NodeX,int NodeY,int D)
{	//此函数作用:1.将Y的根节点的根节点设置为X的根结点
//2.由于设置后RootOfY已经不再是根节点,所以要保证其kind相对于RootOfX的正确性
//在此函数里,D==0说明NodeX与NodeY同类,
//D==1说明NodeX吃NodeY,因此传参前要将D减一

//将Y所在树依附到X所在树上
animal[RootOfY].parent = RootOfX;
//更新RootOfY的kind,保证其kind相对于RootOfX的正确性
//原始公式为animal[NodeX].kind-(animal[NodeY].kind+amimal[RootOfY].kind)=D;
animal[RootOfY].kind = (-D+(animal[NodeX].kind-animal[NodeY].kind)+3)%3;
}

int Find(int NodeToFind)
{	//此函数作用:1找到NodeToFind所在集合,即找到其根节点
//2.查找的过程是一个递归过程,递归出口是遇到一个根节点为自身的结点,即当前集合的根节点
//然后递归返回的路径上依次将各个各个结点的根节点设置为此节点,并继续返回此根结点,这样就可以
//把集合中所有结点的根节点设置为同一个根结点,这叫做“路径压缩”,
//是为了使并查集稳定而做的一种改进,目的是避免并查集成为接近于链表的结构,因为并查集的优势体现在
//树的深度较浅,查找容易,此举可看作对并查集深度的控制。
//递归返回路径上,除了要做更新途径结点(按照距根从近到远的顺序)的根结点外,还要依次修正
//途径结点的kind。这是因为在Union操作中只是保证了直接和根节点相连的
//结点(即未作Union操作前的某一树的根)kind的正确性,其他节点kind的正确性就需要在这里修正
//从近到远进行修正恰好保证了每次修正都有理有据。
//每次修正都要依仗其原根节点kind的正确性,因为这是一个相对计算的关系
if(animal[NodeToFind].parent==NodeToFind)
return NodeToFind;
int temp = animal[NodeToFind].parent;
animal[NodeToFind].parent = Find(animal[NodeToFind].parent);

//更新NodeToFind结点kind的正确性,因为原来的kind是相对于0(根节点kind都为0),
//原来的根节点现在已经不是根节点了,所以只需要根据原根节点现有的kind值即可更新
animal[NodeToFind].kind = (animal[NodeToFind].kind+animal[temp].kind+3)%3;

return animal[NodeToFind].parent;
}

int main()
{
int n,k;
cin>>n>>k;
MakeSet(n);
int x,y,d;
int NumOfLies = 0;
while (k--)
{
//cin>>d>>x>>y;	//cin导致TLE
scanf("%d%d%d",&d,&x,&y);
if(x>n||y>n)
NumOfLies++;
else if(x==y&&d==2)
NumOfLies++;
else
{
int rootx = Find(x);
int rooty = Find(y);
if (rootx==rooty)
{
//如果两节点的根相等,说明根据以往结论,这两点的关系已经确定,可以开始判断
if(d==1&&animal[x].kind!=animal[y].kind)
NumOfLies++;
if(d==2&&(animal[x].kind-animal[y].kind+3)%3!=1)
NumOfLies++;
}
else
{
//x和y不在一个集合里,说明两者关系尚未确定,则将两者关系进行确定,
//即将两者所在集合进行合并操作Union,Union操作只保证了和根结点相连的结点kind
//的正确性,不要担心,由于并查集的特点(根节点表示法的树)所以无法从根向下找到
//子节点,因此子节点即使有错误也是安全的,到了需要访问(Find)的时候再修正也不迟
Union(rootx,rooty,x,y,d-1);	//别忘了d要减一才符合我们规定的含义
}
}
}
cout<<NumOfLies<<endl;
}


 

这个解题报告很好:

 

http://www.cppblog.com/tortoisewu/archive/2009/07/14/85501.html

 

 

1703是1182的简化版

解题思路一样

代码如下:

#include <iostream>
#include <stdio.h>

using namespace std;

struct _cri{
int parent;
int gang;	//0 for the same as SetRoot,1 for different from it
}cri[100010];

void MakeSet(int SizeOfSet)
{
for (int i = 1;i <= SizeOfSet;i++)
{
cri[i].parent = i;
cri[i].gang = 0;
}
}

void Union(int RootOfX,int RootOfY,int NodeX,int NodeY,int SorD)
{
cri[RootOfY].parent = RootOfX;
cri[RootOfY].gang = cri[NodeX].gang==cri[NodeY].gang?SorD:(1-SorD);
}

int Find(int NodeToFind)
{
if(cri[NodeToFind].parent==NodeToFind)
return NodeToFind;
int temp = cri[NodeToFind].parent;
cri[NodeToFind].parent = Find(cri[NodeToFind].parent);
cri[NodeToFind].gang = cri[temp].gang==0?cri[NodeToFind].gang:(1-cri[NodeToFind].gang);
return cri[NodeToFind].parent;
}

int main()
{
int NumOfCases;
cin>>NumOfCases;
while (NumOfCases--)
{
int N,M;
char AorD;
int cri_a,cri_b;
cin>>N>>M;
MakeSet(N);
while (M--)
{
scanf("/n%c %d %d",&AorD,&cri_a,&cri_b);
int rootx = Find(cri_a);
int rooty = Find(cri_b);
if (AorD=='D')
Union(rootx,rooty,cri_a,cri_b,1);
else
{
if (rootx==rooty)
{
if (cri[cri_a].gang==cri[cri_b].gang)
cout<<"In the same gang."<<endl;
else
cout<<"In different gangs."<<endl;
}
else
cout<<"Not sure yet."<<endl;
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构 c