带权并查集(hdu 3635,hdu 2818)

本文探讨了并查集在解决特定问题时的应用与优化策略,包括如何通过并查集解决城市球移动次数与城市内球数量的问题。通过实例演示并查集在不同场景下的高效运用,特别强调了叶子节点到根节点距离的计算方法,以此揭示并查集在实际问题解决中的强大能力。

带权并查集:

http://acm.hdu.edu.cn/showproblem.php?pid=3635

根据题意可得,我们要求某个球移动了多少次,一个城市有多少个球。

对于第二个问题很好解决,当我们用并查集并的时候,将子节点内的球全部赋给父节点,即num[parent]+=num[loof];

第一个问题才是大家纠结的。我们想一下,当将并查集的子节点连接到父节点时即表示子节点内的所有球都移动了一次,并查集最终形成的是一棵树,我们只需要求叶子节点到根节点的距离。叶子节点表示的是球开始的位置,根节点则是球现在所在的位置。这样我们就可以在并查集查原始父节点的时候来求,即每次都加上父节点到父节点的父节点的次数即可以求的。

#include <iostream>
#include <cstdio>
#include <cstring>
#define MAX 10200

using namespace std;

int parent[MAX],Rank[MAX],num[MAX];
int n,m,flag;
void init()
{
	for(int i=1; i<=n; i++)
	{
		Rank[i]=0;
		num[i]=1;          
		parent[i]=i;
	}
	flag++;
}
int find(int x)
{
	if(parent[x]!=x)
	{
		int temp=parent[x];
		parent[x]=find(parent[x]);
		Rank[x]+=Rank[temp];                    //每次加上父节点到父节点的父节点的次数
	} 
	return parent[x];
}
void Union(int a,int b)
{
	int root_a=find(a),
		root_b=find(b);
	if(root_a==root_b) return;
	parent[root_a]=root_b;
	num[root_b]+=num[root_a];                  //子节点的球的全部放到根节点上
	num[root_a]=0;                             //子节点 上的球个数为0
	Rank[root_a]=1;                            //对于每个子节点,并一次即移动一次。
}
void solve()
{
	char ch[2];
	int a,b;
	printf("Case %d:\n",flag);
	for(int i=1; i<=m; i++)
	{
		cin>>ch;
		if(ch[0]=='T')
		{
			cin>>a>>b;
			Union(a,b);
		}
		else
		{
			cin>>a;
			find(a);
			printf("%d %d %d\n",parent[a],num[parent[a]],Rank[a]);
		}
	}
}
int main()
{
	int t;
	cin>>t;
	flag=0;
	while(t--)
	{
		cin>>n>>m;
		init();
		solve();
	}
	return 0;
}

hdu 2818 

http://acm.hdu.edu.cn/showproblem.php?pid=2818

这题数据太坑,说了1~N的,结果包含了0,坑啊

这道题与POJ1988一样

http://poj.org/problem?id=1988

#include <iostream>
#include <cstring>
#include <cstdio>
#define MAX 30100

using namespace std;

int parent[MAX],num[MAX],Rank[MAX];
int n;
void init()
{
    for(int i=0; i<MAX; i++)
    {
        parent[i]=i;
        num[i]=1;                //表明树桩上有多少个石块
        Rank[i]=0;               //石块i下面有多少石块
    }
}
int find(int x)
{
    if(parent[x]!=x)
    {
        int temp=parent[x];
        parent[x]=find(parent[x]);
        Rank[x]+=Rank[temp];
    }
    return parent[x];
}
void Union(int a,int b)
{
    int root_a=find(a),
        root_b=find(b);
    if(root_a==root_b) return;
    parent[root_a]=root_b;
    Rank[root_a]=num[root_b];          //让连接到根节点的节点的值等于根节点上的石块数目,这样可以在查操作的时候更新每个石块下有多少石块
    num[root_b]+=num[root_a];           //将所有子节点上的石块移动到根节点
    num[root_a]=0;
}
void solve()
{
    char ch[2];
    int a,b;
    for(int i=1; i<=n; i++)
    {
        cin>>ch;
        if(ch[0]=='M')
        {
            cin>>a>>b;
            Union(a,b);
        }
        else
        {
            cin>>a;
            find(a);
            printf("%d\n",Rank[a]);
        }
    }
}
int main()
{
    while(cin>>n)
    {
        init();
        solve();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值