· Disjoint-set algorithm notes

Disjoint-set

Disjoint-set I personally think that for processing a certain special data structure algorithms, the advantage that the program brief , can quickly and concisely express the dots, the number of the relationship between the number.

This algorithm has two operations merge and query

  • Merge: can be high time will meet some of the data requirements of the subject combined in a set of
  • Query: the ability to time-sensitive queries whether a specified data exists in a collection being, or is calculated to meet the requirements of the subject the number of collection

So this is how the algorithm work?

Next we look at a few examples

1. relatives example

Luogu Portal

This is a very simple disjoint-set entry title, and here is used to check the operation of ideas about the set.

According meaning of the questions, if A, B relatives, C B relatives, then ABC three relatives of each other, that ABC three nodes can be placed in the same collection which, for the convenience of presentation, we can think of this tree, a is the root, B is a child node of a, C is a child node of B.

Disjoint-set .png

As a result, we will be able to take advantage of disjoint-set, there will be two nodes of relatives, were built side , there are two nodes is about to relatives of the collection which are combined in a query, as long as the use of a recursive , non-stop asked up until asked their root , root as the same, there is kinship.

However, there is a problem, so down a point to ask up again, it will spend a lot of time, then how can a high time to say it?

Here we need to do a disjoint-set optimized , so that it can be faster to calculate their root, I call it shrink point , all the points are related directly linked , that is, to construct a tree of depth 2, as shown in FIG.

Disjoint-set optimization .png

Thus, when a query can have direct access to their root a.

#include<stdio.h>
#include<algorithm>
int n,m,a,b,fa[10005];
int find(int x){   //查询函数
    if(fa[x]!=x) fa[x]=find(fa[x]);  //缩点:反复向上询问,询问到根节点才赋值
    return fa[x];
}
int main(){
    scanf("%d%d",&n,&m);
    for(register int i=1;i<=n;i++){fa[i]=i;} //初始化,开始时每个节点都是一个根节点
    for(register int i=1;i<=m;i++){
        scanf("%d%d",&a,&b);
        a=find(a);b=find(b);
        if(a!=b) fa[b]=fa[a];  //合并
    }
    int Q;
    scanf("%d",&Q);
    for(register int i=1;i<=Q;i++){
        scanf("%d%d",&a,&b);
        if(find(a)==find(b)) printf("Yes\n");  //根节点相同
        else printf("No\n");
    }
    return 0;
}

By This question, we can understand the basic operating principle disjoint-set, in fact, many of the basic codes in accordance with this title is also used as a template , but made some changes on this basis.

2. examples triad gangs

Luogu Portal

From the meaning of the questions, this question with a question on the big question frameworks, changes in place is

  • Seeking the number of collection
  • More out of the new concept, the enemy of my enemy is my friend

By the new concept will be extended to many new relationships, slightly rearranged, there follows a few

  • My friend's friend is my friend
  • My friend's enemy is my enemy
  • My enemy's enemy is my friend
  • My enemy's enemy is my friend

Then if only a single set of the original calculation, will find it difficult to exhibit the above four cases , in the previous question, there are only two cases, are my relatives or not, and in this example, there are three kinds of circumstances, a friend, an enemy, or both does not matter.

在上一题中我们用了一棵图来描述人与人之间的亲戚关系,两点之间有建边即为亲戚关系,无建边则没有关系,也就是将有亲戚关系的集合互相合并,那么在这一题中,我们依然可以沿用这样的思路,不过这题我们需要两张图,一张用以表现朋友关系,一张用以表现敌人关系,两张图中的节点可以互相映射

那么接下来就是编写代码,我们个可以使用n的空间表示朋友,n的空间表示敌人,总共使用2*n的空间。

如何编写两棵树之间节点互相的关系映射,假设两人A,B,当A与B是朋友时,在朋友关系图中将B点与A点建边,另外在B的敌人关系图中查找B的敌人,在敌人关系图中将该节点与A节点建边。

假设A与B是朋友,A与C是朋友,B与D是敌人,如图

Gang .png

反之,亦可成立,即A与B是敌人时,将B点与A点在敌人关系树中建边,同时,在敌人关系树图中寻找与A有关的节点(除了B),在朋友关系图中将该节点与B节点建边。

假设A与B是敌人,A与C是敌人,B与D是朋友,如图

Gang 2.png

假设表示朋友的集合是P,P的反集即表示敌人的集合是D

则用数学的集合语言来表示就是

  • A与B是朋友:
    1. \(P(A) \cup P(B)\) 朋友的朋友是朋友
    2. \ (D (A) \ cup D (B) \) friend's enemy is the enemy
  • A and B are the enemy:
    1. \ (F (A) \ cup P (B) \) enemy's enemy is my friend
    2. \ (D (A) \ cup F (B) \) enemy's friend is the enemy

Then, re-use shrink point to improve the operating efficiency of the code, you can achieve "Ac of the real!"

But how to achieve the above conversion relationship?

2 * N array Fa open space and corresponding to n + 1,2 1 corresponding to n + 2,3 corresponding to n + 3, and so on, which is represented by a set of 1-n P, n + 1-n + n represents the P anti-set, a collection of D, and calculate the number of gang just about to enumerate all the points, take a look at a few points fa array has not been modified since shrinking point in the future, the root node must correspond to his own number, so the calculation of the root node is the number of collections.

The Code!

#include <stdio.h>
#include <algorithm>
#include <iostream>
int fa[50005];
int find(int a){
    if(fa[a]!=a) fa[a]=find(fa[a]);  //缩点
    return fa[a];
}
int main(int argc, char const *argv[]){
    int n,m;scanf("%d%d",&n,&m);
    for (register int i = 1; i <= 2*n; ++i){ fa[i]=i; }  //初始化
    for (register int i = 1; i <= m; ++i){
        char cmd;std::cin>>cmd;
        int a,b;scanf("%d%d",&a,&b);
        if (cmd =='F'){
            fa[find(a)]=find(b);   //朋友集合相并
            fa[find(a+n)]=find(b+n);  //敌人集合相并
        }else{
            fa[find(a+n)]=find(b);  //敌人的朋友是敌人
            fa[find(b+n)]=find(a);  //敌人的敌人是朋友
        }
    }
    int ans=0;
    for (register int i = 1; i <= n; ++i){
        if(fa[i]==i) ans++;  //枚举集团数量
    }
    printf("%d",ans);
    return 0;
}

There are examples to expand again after it www

We can see by the above examples, in fact, the essence of disjoint-set is to seek common ground , will have the same characteristics or relationship data for a merger in the same sets, and check the amount of code sets is not large, but it can clearly the data describing the relationship we need.

I personally think disjoint-set is a very good algorithm, short and efficient , it is worth to understand and study, the problem is that you can not go to reasonable use, good use is a weapon, cut iron drunk.

+ Thumbs concern is your greatest encouragement to me!

Guess you like

Origin www.cnblogs.com/lightcoder/p/11425869.html