[数据结构]第十章-并查集

来源:互联网 发布:nginx conf 配置php 编辑:程序博客网 时间:2024/06/02 05:03

并查集 : 以不相交集合为基础的抽象数据类型

· 支持运算

1.UFunion(A,B,U):将并查集U中的集合A和B合并,其结果取名为A或B
2.UFfind(e):找出包含元素e的集合,并返回该集合的名字

· 实现思想

每个集合用一棵树表示,树的结点用于存储集合中的元素名和一个指向其父节点的指针,树根结点的元素代表该树所表示的集合。

· 用父节点数组实现

数组 parent[x] 表示元素的父节点。

但在最坏情况下,n个结点的树可能退化为一条链。此时对所有元素各执行一次UFfind耗时O(n^2)。
改进方法:①按结点个数合并(结点少的合并到多的上,用parent[root] = -n记录根为root的树的结点个数n);②按高度合并(高度低的合并到高的上,记录方式同上);③按压缩路径(O(n(α(n))),α(n) < 4)

下面是泡泡的并查集模板

struct UF    {     int fa[MAX_N];     void init() { for(int i = 1; i < MAX_N; i++) fa[i] = i;}     int find(int a) { return fa[a] == a ? a : fa[a] = find(fa[a]);}     void mix(int a,int b) { fa[find(a)] = find(b);}     bool isSame(int a, int b) { return find(a) == find(b);} } uf;

//–作业题

12.4 山海经

这里写图片描述
这里写图片描述

(题目很复杂其实只是想问两节点间的差值,有些已经能判断有些还不能,也即集合间的关系为是否已经能判断差值,能互相判断差值的扔进同一集合。如果AB间差值还不能判断,则语句d A B则把AB集合合并下并且AB结点之间差值为d。如果已经能判断则运算下是否为d以判断真假。差值的算法为,记录下集合中每个结点到根节点的差值qua,那么同一集合中的两节点ab就能通过a-fa-b的路径得到,在合并集合的时候更新a集合根节点的fa为b并且fa的qua通过fa-a-b-fb更新,合并的时候只要更新根节点就好,在find中回溯一边更新其他节点的qua。)

#include<iostream>  #include<cstdio>  using namespace std;  #define MAX 50000+7  int pre[MAX];  int qua[MAX];//与pre的犇势差   int m,n,k,i;  int find(int x)  {      int t;      if(x != pre[x])      {          t = pre[x];          pre[x] = find(pre[x]);          qua[x] = (qua[x] + qua[t])%m;  //回溯更新quax       }      return pre[x];  }  void mix(int a, int b,int d)  {      int fa = find(a);      int fb = find(b);      if(fa != fb)          pre[fa] = fb;      qua[fa]=(m-qua[a]+d+qua[b])%m;  //fa-a-b-fb 更新quafa   }  int main()  {         scanf("%d%d%d",&m,&n,&k);      for(i = 1; i <= n; i++)      {          pre[i] = i;          qua[i] = 0;      }      int d,a,b;      int ans = 0;      for(i = 1; i <= k; i++)      {          scanf("%d%d%d",&d,&a,&b);          if(a > n || b > n || d >= m )          {              ans++;              continue;          }          int fa = find(a);          int fb = find(b);          if(fa == fb)          {              if((qua[a]+m-qua[b])%m != d)  //a-f-b ab犇势差不为d                   ans++;          }          else mix(a,b,d);      }      printf("%d\n",ans);      return 0;  }

12.2 直通车

这里写图片描述
这里写图片描述
这里写图片描述

(朋友的关系可传递,敌人的关系不可传递。所以用并查集记录朋友关系,用二维数组记录敌人关系即可。)

#include<iostream>  #include<cstdio>  using namespace std;  #define MAX 100+7  int pre[MAX];  int mp[MAX][MAX];  int n,m,k,i,j;  void init()  {      int i;      for(i = 1; i <= n; i++)          pre[i] = i;  }  int find(int x)  {      int r = x;      while(r != pre[r])          r = pre[r];      int i = x,j;      while(i != pre[i])      {          j = pre[i];          pre[i] = r;          i = j;      }      return r;  }  void mix(int a, int b)  {      int fa = find(a);      int fb = find(b);      if(fa != fb)          pre[fa] = fb;  }  int main()  {      scanf("%d%d%d",&n,&m,&k);      init();      int x,y,op;      for(i = 1; i <= m; i++)      {          scanf("%d%d%d",&x,&y,&op);          mp[x][y] = op;          mp[y][x] = op;          if(op == 1) mix(x,y);      }      for(i = 1; i <= k; i++)      {          scanf("%d%d",&x,&y);          int fx = find(x);          int fy = find(y);          if(fx == fy && mp[x][y] != -1) printf("Good job\n");          else if(fx != fy && mp[x][y] != -1) printf("No problem\n");          else if(mp[x][y] == -1)          {              if(fx == fy) printf("OK but...\n");              else printf("No way\n");          }      }      return 0;  }
0 0