您的位置:首页 > 大数据 > 人工智能

Hdu 2473 Junk-Mail Filter (并查集的删除)

2014-02-14 11:37 531 查看
题意:有n个集合,m个操作。对每个操作输入的如果为M,那就合并两个集合,否则将此点分离出去,问一共有多少个集合。

举例:对于

M 0 2

M 1 2

S 2

合并后的集合是{0,1,2},把2删除掉之后, 集合变为 {0,1}

思路:建立一个映射,通过改变将删除点的映射关系,同时改变其父节点到一个新的、虚设的点。这种方法很费空间。

直接将一个点的父节点改成自身是不行的,因为如果这点已经是一个集合的根节点,那它的父节点本来就等于自身。

#include <cstdio>
#include <cstring>

const int N=1100005;

class Disjoint_Set
{
public:
	int father
;     /*father[x]表示x的父节点*/
	int rank
;       //以该节点为根节点的点数,同时起到按秩合并的作用
	int mapping
;     //节点映射数组
	int id;

	void Init (int n)
	{//本题对应元素为0~n-1
		for (int i=0;i<n;i++)
			Make_Set (i);
        id=n;        //////
	}

	void Make_Set (int x)
	{
		father[x]=x;
		mapping[x]=x;
		rank[x]=1;
	}

	int Find_Set (int x)
	{
		if (x != father[x])
			father[x] = Find_Set(father[x]);//回溯
		return father[x];
	}

	void Union (int x,int y)
	{
		int a=Find_Set (x);
		int b=Find_Set (y);
		if (a == b)
			return;
		if (rank[a] >= rank[b])
		{
			father[b]=a;
			rank[a]+=rank[b];
		}
		else
		{
			father[a]=b;
			rank[b]+=rank[a];
		}
	}

	void Delete (int x)
	{//改变待删除点的映射数组
	    father[id]=id;
	    mapping[x]=id++;
    }
}ob;

bool visit
;

int main ()
{
	int n,m,Cas=1;
	while (scanf("%d%d",&n,&m),n||m)
	{
		ob.Init(n);
		char str[4];
		int a,b;
		while (m--)
        {
            scanf("%s%d",str,&a);
            if (str[0]=='M')
            {
                scanf("%d",&b);  //利用映射值
                ob.Union(ob.mapping[a],ob.mapping[b]);
            }
            else
                ob.Delete(a);
        }
        memset(visit,false,sizeof(visit));
        int ans=0;
        for (int i=0;i<n;i++)
        {
            a=ob.Find_Set(ob.mapping[i]);
            if (visit[a]==false)
            {
                ans++;
                visit[a]=true;
            }
        }
        printf("Case #%d: %d\n",Cas++,ans);
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: