并查集

    xiaoxiao2023-11-23  165

    上次图论那个……还没写完……疯了,最近好多作业啊!特别是那个程序,好难!好痛苦,嘤嘤嘤。

    基本内容:

    并查集(union-find set)是一种用于分离集合操作的抽象数据类型。它所处理的是“集合”之间的关系,即动态地维护和处理集合元素之间复杂的关系,当给出两个元素的一个无序对(a,b)时,需要快速“合并”a和b分别所在的集合,这其间需要反复“查找”某元素所在的集合。“并”、“查”和“集”三字由此而来。在这种数据类型中,n个不同的元素被分为若干组。每组是一个集合,这种集合叫做分离集合(disjoint set)。并查集支持查找一个元素所属的集合以及两个元素各自所属的集合的合并。

    通常来说,需要看某些元素之间是否有关联,并且这种关联可以继承(1元素跟2元素有关联,那么与1元素有关联的所有元素都与2有关联。这样的题目就可以用并查集来解决这个问题。

    并查集的数据结构记录了一组分离的动态集合S={S1,S2,…,Sk}。每个集合通过一个代表加以识别,代表即该元素中的某个元素,哪一个成员被选做代表是无所谓的,重要的是:如果求某一动态集合的代表两次,且在两次请求间不修改集合,则两次得到的答案应该是相同的。

    即每个数据都有一个标记,这个标记以一个有关联的一组数中的一个数决定,通常将所有的数以一种关联关联上这个数值。

    基本思想:

    动态集合中的每一元素是由一个对象来表示的,设x表示一个对象,并查集的实现需要支持如下操作:

    MAKE(x):建立一个新的集合,其仅有的成员(同时就是代表)是x。由于各集合是分离的,要求x没有在其它集合中出现过。

    UNIONN(x,y):将包含x和y的动态集合(例如Sx和Sy)合并为一个新的集合,假定在此操作前这两个集合是分离的。结果的集合代表是Sx∪Sy的某个成员。一般来说,在不同的实现中通常都以Sx或者Sy的代表作为新集合的代表。此后,由新的集合S代替了原来的Sx和Sy。

    FIND(x):返回一个指向包含x的集合的代表。

    代码实现:

    #include<iostream> #include<cstdio> using namespace std; #define maxn 20001 int father[maxn]; int m,n,i,x,y,q; /* int find(int x) //用非递归的实现 {    while (father[x] != x) x = father[x];    return x; } */ int find(int x) //用递归的实现 { if (father[x] != x) return find(father[x]); else return x; }   void unionn(int r1,int r2) { father[r2] = r1; } int main() {    cin >> n >> m;    for (i = 1; i <= n; i++) father[i] = i; //建立新的集合,其仅有的成员是i    for (i = 1; i <= m; i++)   {  scanf("%d%d",&x,&y); int r1 = find(x); int r2 = find(y); if (r1 != r2) unionn(r1,r2); }   cin >> q;   for (i = 1; i <= q; i++) {   scanf("%d%d",&x,&y);   if (find(x) == find(y)) printf("Yes\n");   else printf("No\n");  } return 0; }

    但是当单练太长时,这样做必然会超时。所以我们必须进行优化:

    。#include<iostream>   #include<cstdio>   using namespace std;   #define maxn 20001   int father[maxn];   int m,n,i,x,y,q;   /*   int find(int x) //用非递归的实现   {    while (father[x] != x) x= father[x];    return x;   }   */   int find(int x) //用递归的实现   {    if (father[x] != x) father[x] = find(father[x]); //路径压缩,直接赋值成父亲    return father[x];   }   void unionn(int r1,int r2)   {    father[r2] = r1;   } int main()   {    freopen("relation.in","r",stdin);    freopen("relation.out","w",stdout);    cin >> n >> m;    for (i = 1; i <= n; i++)    father[i] = i; //建立新的集合,其仅有的成员是i    for (i = 1; i <= m; i++)    {    scanf("%d%d",&x,&y);    int r1 = find(x);    int r2 = find(y);    if (r1 != r2) unionn(r1,r2);    }    cin >> q;    for (i = 1; i <= q; i++)    {    scanf("%d%d",&x,&y);;    if (find(x) == find(y)) printf("Yes\n");    else printf("No\n");    }    return 0;   }

    例题:

     CodeForces - 218C

    代码:

    #include<bits/stdc++.h> using namespace std; struct dian { int x,y; }d[2000]; int f[2000],n; int Find(int x) { if(f[x]==x) return x; return f[x]=Find(f[x]); } void Union(int a,int b) { int r=Find(a); int l=Find(b); if(r!=l) f[a]=b; } int main() { cin>>n; for(int i=1;i<=n;i++) { f[i]=i; } for(int i=1;i<=n;i++) { cin>>d[i].x>>d[i].y; } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(d[i].x==d[j].x||d[i].y==d[j].y) Union(i,j); } } int ans=0; for(int i=1;i<=n;i++) { if(f[i]==i) ans++; } cout<<--ans<<endl; return 0; }

     

    最新回复(0)