zoukankan      html  css  js  c++  java
  • 并查集

    并查集是一类十分常用的数据类型,它有着十分广泛的应用。在信息竞赛中,它主要执行的操作一般有三种。
    (1) 合并a,b两个元素所在的集合 Merge(a,b)
    (2)查找某个元素属于哪个集合 find(k)
    (3)查询两个元素是否属于同一集合 Query(a,b)

    函数模板
    (1)find

    int find(int x){    
    if(fa[x]==x) return x; //找到即返回
    int t=find(fa[x]);    //继续递归find
    return t;
    } 


    (2)Merge

    void merge(int x,int y){
    x=find(x);
    y=find(y);
    if(x==y) return; //根相同,无需合并,即返回
    fa[x]=y; //根不同,即合并
    }

    对于find和merge的优化
    (1)
    对于merge:启发式合并(使用次数少,代码略过)
    在合并集合S1、S2的时候,我们让较小的树成为较大的树的子树。这里可以是深度、节点个数等启发函数来比较树的大小(一般使用深度)。

    (2)
    对于find:路径压缩(常用,效率高,代码简单)
    我们在查找完u至根节点的路径之后,一般将这条路径上的所有节点的父节点都设为根节点,这样可以大大减少之后的查找次数。

    int find(int x){
    if(fa[x]==x) return x;
    int t=find(fa[x]);
    fa[x]=t; //记录路径上的节点成为父节点,减少查询次数
    return t;
    }

    时间复杂度分析:
    可以证明,经过启发式合并和路径压缩之后的并查集,执行m次查找的复杂度为O(mα(m))
    注α(m):Ackermann函数的某个反函数,可以近似的认为它是小于5的。所以并查集的单次查找操作的时间复杂度也几乎是常数级的。

    例题:
    程序自动分析
    题目描述:
    在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足。
    考虑一个约束满足问题的简化版本:假设x1,x2,x3,…代表程序中出现的变量,给定n个形如xi=xj或xi≠xj的变量相等/不等的约束条件,请判定是否可以分别为每一个变量赋予恰当的值,使得上述所有约束条件同时被满足。例如,一个问题中的约束条件为:x1=x2,x2=x3,x3=x4,x1≠x4,这些约束条件显然是不可能同时被满足的,因此这个问题应判定为不可被满足。
    现在给出一些约束满足问题,请分别对它们进行判定。
    注:1≤n≤1000000
    样例:

    看输入的是1,我们把它加到一个并查集里去,是0,我们暂且不管。操作好以后,我们特判是0,但树根相同的这种情况,显然是不成立的。标记下来,一个个输出就结束了。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define N 1000010
    using namespace std;
    int fa[N];
    int find(int x)    //寻找函数 
    {
    if(fa[x]==x) return x;
    int t=find(fa[x]);
    fa[x]=t;
    return t;
    //return fa[x]==x?x:fa[x]=find(fa[x]);
    }
    void merge(int x,int y)    // 合并函数 
    {
    x=find(x);
    y=find(y);
    if(x==y) return;
    fa[x]=y;
    }
    int m,x[N],y[N],f[N];
    void doit()
    {
    memset(fa,0,sizeof(fa));    //记得清空 
    for(int i=1;i<=1000000;i++) fa[i]=i;    //初始化 
    cin>>m;
    for(int i=1;i<=m;i++)
    {
    cin>>x[i]>>y[i]>>f[i];
    if(f[i]==1) merge(x[i],y[i]);    // 合并 
    }
    bool ans=true;
    for(int i=1;i<=m;i++)
    {
    if(f[i]==0)
    {
    if(find(x[i])==find(y[i])) ans=false;    //如果在一个并查集里,但不是1,那就显然不成立,进行标记 
    }
    }
    puts(ans?"YES":"NO");
    }
    int main()
    {
    int T;
    cin>>T;
    while(T--) doit();
    }
  • 相关阅读:
    [sql查询] 重复数据只取一条
    SSIS,参数坑
    数据仓库之建立多维数据库
    数据仓库之SSIS开发
    开发规范
    页面以base64输出图片
    内嵌iframe
    T-Sql编程基础
    MVC3.0----整理之一
    原生JS 表单提交验证器
  • 原文地址:https://www.cnblogs.com/SKTskyking/p/12632192.html
Copyright © 2011-2022 走看看