poj1703

来源:互联网 发布:淘宝小类目让我发财了 编辑:程序博客网 时间:2024/05/17 22:54

并查集水题。循环路径并查集

关键是合并关联集合,分析如下:现有关系A a,b(a与b属于不同集合)

那么现在要做的就是将a关联集合与b关联集合和并,问题:如何合并?

必定是对父节点合并,那么转换关系如何?

分析如下:

路径压缩后,令deep[a]表示a的深度,其根节点为x,令deep[b]表示b的深度,其根节点为y,那么若将x集合与y集合合并(假设合并后根节点为x),则合并后b的深度应该为:

deep[b]=(deep[a]+1)%2;而此时deep[b]=deep[y]+deep[b],故有deep[y]+deep[b]=(deep[a]+1)%2;也即deep[y]=(deep[a]+1-deep[b])%2,这样就将x集合和y集合合并在一起了,这是核心,接下来主函数就只需要判断深度关系即可判断对应的关系。典型的循环路径压缩并查集。

下面是代码:940K+360MS

#include <stdio.h>#include <stdlib.h>#include <string.h>#define Max 100010#define Maxx(a,b) (a)>(b)?(a):(b)#define Min(a,b) (a)<(b)?(a):(b)int set[Max];int deep[Max];int c,n,m;int find(int x){if(x==set[x])return x;int temp=find(set[x]); // 递归寻找根节点deep[x]=(deep[x]+deep[set[x]])%2; //更新深度set[x]=temp; //路径压缩return temp; //返回根节点}int main(){scanf("%d",&c);while(c--){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){ //初始化深度为0set[i]=i;deep[i]=0;}int a,b,x,y;while(m--){getchar();char temp=getchar();if(temp=='A'){ //若为A,则检查关系scanf("%d%d",&a,&b);x=find(a),y=find(b);if(x!=y) //若还没有产生关系printf("Not sure yet.\n");else{ //否则必定存在关系if(deep[a]!=deep[b]) // 若深度相同,则说明是同类printf("In different gangs.\n");else //否则不是同类printf("In the same gang.\n");}}else{scanf("%d%d",&a,&b);x=find(a),y=find(b); //查找根节点if(x<y){ //大并小deep[y]=(deep[a]+1-deep[b])%2; //路径更新set[y]=x;}else if(y<x){ deep[x]=(deep[b]+1-deep[a])%2;set[x]=y;}}}}return 0;}


 

0 0
原创粉丝点击