POJ 1703 并查集

题意:

有两黑帮,N个人,现在又一些信息,你需要根据这些信息确定这两个人之间是否属于同一个黑帮。有两种输入: D a b 表示a,b不属于同一个黑帮;A a b 询问a,b是否属于同一个黑帮,可以回答属于,不属于,不确定三种。
题解:

可以用带权的并查集解决。若a,b同属于一个集合说明二者的关系已经确定,输出属于或者不属于;否则输出不确定。当a,b同属于一个集合时,可以通过权值来判断它们的具体关系。注意:这里的权值仅仅代表某个节点在压缩森林中与其根节点的距离,并不代表它在原森林中的深度。

 

#include<cstdio>
using namespace std;
#define MAX 200000
int father[MAX], weight[MAX], rank[MAX];
bool check[MAX];

void init ( int n )
{
    for ( int i = 1; i <= n; i++ )
    {
        father[i] = i;
        weight[i] = 0;
        rank[i] = 1;
        check[i] = false;
    }
}

int Find ( int x, int& w ) //节点到压缩森林中根节点的距离 = 父节点到根的距离 + 本身到父节点的距离
{
    if ( father[x] != x )
    {
        father[x] = Find ( father[x], w );
        weight[x] = (weight[x] + w) % 2;
        w = weight[x];
    }
    else w = 0;
    return father[x];
}

int Find_Depth ( int x )
{
    int a, w;
    if ( x == father[x] ) //根节点到自己的距离为0,直接返回
        return weight[x];
    a = Find ( x, w );
    return weight[a] + weight[x]; //节点到压缩森林中根节点的距离 = 父节点的距离 + 本身到父节点的距离
}

void Link ( int x, int y ) //按秩结合
{
    int a, b, w;
    a = Find ( x, w );
    b = Find ( y, w );
    if ( a == b ) return;
    if ( rank[a] >= rank[b] )
    {
        weight[b] = (weight[x] + 1 + weight[y]) % 2;
        rank[a] += rank[b];
        father[b] = a;
    }
    else
    {
        weight[a] = (weight[x] + 1 + weight[y]) % 2;
        rank[b] += rank[a];
        father[a] = b;
    }
}

int main()
{
    char ch[4];
    int n, m, a, b, w, cs;
    scanf("%d",&cs);
    while ( cs-- )
    {
        scanf("%d%d",&n,&m);
        init ( n );
        while ( m-- )
        {
            scanf("%s %d %d",ch, &a, &b);
            if ( ch[0] == 'D' )
                Link ( a, b );
            else
            {
                if ( Find(a,w) != Find(b,w) )
                    printf("Not sure yet.\n");
                else if ( Find_Depth(a) % 2 == Find_Depth(b) % 2)
                    printf("In the same gang.\n");
                else
                    printf("In different gangs.\n");
            }
        }
    }
    return 0;
}




在网上还看到另一种解法:

 

#include <iostream>
using namespace std;

#define N 100005
int opp[N], father[N], rank[N];
int n, m;

void make_set ( int x )
{
	for ( int i = 0; i <= x; ++i )
	{
		father[i] = i;
		opp[i] = rank[i] = 0;
	}
}

int find_set ( int x )
{
	if ( father[x] != x )
		father[x] = find_set(father[x]);
	return father[x];
}

void Union ( int x, int y )
{
	int fx = find_set(x);
	int fy = find_set(y);
	if ( fx == fy ) return;
	if ( rank[fx] > rank[fy] )
		father[fy] = fx;
	else
		father[fx] = fy;
	if ( rank[fx] == rank[fy] )
		rank[fy]++;
}

int main()
{
	int t, n, m, x, y;
	char ask;
	scanf("%d",&t);
	while ( t-- )
	{
		scanf("%d%d",&n,&m);
		make_set(n);
		while ( m-- )
		{
		    getchar();
		    scanf("%c",&ask);
			scanf("%d%d",&x,&y);
			if ( ask == 'A' )
			{ 
				if ( find_set(x) == find_set(y) )
					printf("In the same gang.\n");
				else if ( find_set(x) == find_set(opp[y]) )
					printf("In different gangs.\n");
				else
					printf("Not sure yet.\n");
			}
			else
			{
				if ( opp[x] == 0 && opp[y] == 0 )
				{
					opp[x] = y;
					opp[y] = x;
				}
				else if ( opp[x] != 0 && opp[y] == 0 )
				{
					opp[y] = x;
					Union(y,opp[x]);
				}
				else if ( opp[x] == 0 && opp[y] != 0 )
				{
					opp[x] = y;
					Union(x,opp[y]);
				}
				else if ( opp[x] != 0 && opp[y] != 0 )
				{
					Union(x,opp[y]);
					Union(y,opp[x]);
				}
			}
		}
	}
	return 0;
}









你可能感兴趣的:(POJ 1703 并查集)