并查集之卡片问题

卡片问题:

N个孩子,每个人力量为Xi,第一个孩子为1,第二个孩子为2,从一个盒子中随机抽取M张卡片,每张卡片有一对数字,表示同一组孩子们的位置,加入一张卡片有[1,4],另一张卡片上有[4,3],则有位置[1,3,4]的孩子属于同一组,每组实力以改组所有孩子之和,实力最强的组获胜,求获胜队伍的权利值。
对于这个问题,我首先想到的是图里面的知识,这不就是在不连通的图中找到各个连通的子图嘛,然后开始的想法是通过一个二维矩阵将各个节点有连接则用1标记,没有连接则为0。那么如果判断两个节点之间是否连通只需要判断是否属于同一行或者同一列就可以了,但是后来发现错了,如果遇到{1,2},{2,3},{3,4}这样的卡片,就不可能让所有的孩子都在同一行或列,所以思路是错的。
之后我就一直试图用图里面的知识解决,但我目前编程还是用c编,c语言对于处理一些数组大小问题比较恶心,反正考虑了很久也没弄出来,之后在网上搜了一下,发现用并查集来做这个题目是最好不过的,所以下面简单介绍一下并查集的概念。

并查集

首先介绍一下并查集的思想,就拿上面的问题为例,我们拿到一张卡片,怎么知道这两个孩子是否属于某个集合呢??我们需要找到每个孩子的根节点,通俗的说法就是把每个孩子集合作为一个帮派,根节点就是帮派的孩子王,如果两个孩子的孩子王是一样的,说明这两个孩子已经属于一个集合了;如果不一样,由于这两个孩子已经有了联系,我们需要将这两个帮派合并,合并的规则是怎样呢??首先我们需要看这两个孩子的王底下有多少层的下属关系,层结构少的合并入层多的帮派,如果两个孩子王层的关系是一样的,那就第二个孩子王合并入第一个孩子王,然后第一个孩子王的层结构加一,这样做有什么好处呢??我开始也在想直接随便在两个孩子王里面定一个孩子王起到的效果是一样的呀,都能实现该算法。确实能实现,但是这样做的结果可能会造成单支树的结果,就是树结构变得很长,我们在搜索孩子王的时候就消耗时间复杂度,这其实和红黑树的原理差不多,就是构造平衡二叉树。
那么并查集怎么实现呢??主要分为如下3个步骤:
(1)对集合初始化
(2)查找根节点
(3)合并树节点
一、对集合初始化
在编程之前,我们首先要选择用那种数据结构比较合适,我建议使用结构体,编程起来思路会很清晰,当然你也可以使用数组,其实效果差不多,个人偏向于结构体。
typedef struct node_list
{
	int data;		//权值
	int rank;		//等级
	int parent;		//指向父节点
}node;
其中data记录孩子的权利值,rank代表该孩子的等级,parent代表孩子的父节点
对集合初始化,将每个孩子的data值赋为权利值,rank都为0,parent为自己。
二、查找根节点
查找根节点其实比较简单,一直往上找就可以了,具体程序如下:
int get_parent(node *x,int y)
{
	if(x[y].parent==y)
	{
		return y;
	}
	return get_parent(x,x[y].parent);
}
三、合并树节点
方法其实和原理一样,直接贴代码吧:
        a1=get_parent(child,M[i][0]-1);		//找到根节点
		a2=get_parent(child,M[i][1]-1);
		if(a1!=a2)
		{
			if(child[a1].rank>=child[a2].rank)		//谁的子节点多,谁作为合并后的根节点
			{
				child[a2].parent=a1;
				child[a1].data+=child[a2].data;
				if(child[a1].rank==child[a2].rank)
				{
					child[a1].rank++;
				}
			}
			else
			{
				child[a1].parent=a2;
				child[a2].data+=child[a1].data;
			}
		}
下面是解决上面问题的代码:
#include"stdio.h"
#define N 5				//孩子数目
#define card 3
typedef struct node_list
{
	int data;		//权值
	int rank;		//等级
	int parent;		//指向父节点
}node;
int get_parent(node *x,int y);
void main()
{
	node child[N];		//定义孩子节点
	int X[N]={2,4,5,6,2};
	int M[card][2]={{1,2},{4,1},{4,5}};
	int i=0;
	int a1,a2;					//记录根节点
	int max=0;
	for(i=0;i<N;i++)        //孩子集合初始化
	{
		child[i].data=X[i];
		child[i].parent=i;
		child[i].rank=0;
	}
	for(i=0;i=child[a2].rank)		//谁的子节点多,谁作为合并后的根节点
			{
				child[a2].parent=a1;
				child[a1].data+=child[a2].data;
				if(child[a1].rank==child[a2].rank)
				{
					child[a1].rank++;
				}
			}
			else
			{
				child[a1].parent=a2;
				child[a2].data+=child[a1].data;
			}
		}
	}
	for(i=0;imax)
		{
			max=child[i].data;
		}
	}
	printf("%d",max);
	getchar();
}
int get_parent(node *x,int y)
{
	if(x[y].parent==y)
	{
		return y;
	}
	return get_parent(x,x[y].parent);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值