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

    感谢cyanigence-oi大佬(Orz)的题单

    模板 

    并查集 模板题

    预处理过程:void pre(),赋值数组a

    假设自己就是祖先,则用for循环,来储存每个父亲节点的值

    void pre()
    {
        for(int i=1;i<=n;i++) a[i]=i;
    }

    查询过程:int find(int k)

    用递归的形式一层一层的探索自己的祖先,根节点表示的就是祖先,判断两个人是不是同一个祖先,只要查询各自的根节点是否相同即可

    int find(int k)
    {
        if(a[k]==k) return k;
        else return a[k]=a[find(k)];
    }

    合并过程:void merge(int u,int v)

    void merge(int u,int v)
    {
        a[find(u)]=find(v);
    }

    按秩合并

    P1551 亲戚

    #include <bits/stdc++.h>
    using namespace std ;
    typedef long long ll;
    const int maxn = 2e5 + 10;
    typedef long long ll;
    ll n,m,p;
    int a[maxn];
    void pre()    //预处理
    {
        for(ll i=1;i<=n;i++)
        {
            a[i]=i;
        }
    }
    ll find(ll k)    //查询+路径压缩
    {
        if(a[k]==k) return k;
        else return a[k]=find(a[k]);
    }
    void merge1(ll u,ll v)    //合并路径
    {
        a[find(u)]=find(v);
    }
    int main()
    {
        scanf("%lld%lld%lld",&n,&m,&p);
        pre();
        ll x,y;
        for(ll i=1;i<=m;i++)
        {
            scanf("%lld%lld",&x,&y);
            merge1(x,y);
        }
        for(ll i=1;i<=p;i++)
        {
            scanf("%lld%lld",&x,&y);
            if(find(x)==find(y))
                printf("Yes
    ");
            else
                printf("No
    ");
        }
    }

    P2814 家谱

      用标准库的map

    #include <bits/stdc++.h>
    using namespace std;
    map<string,string>p;
    string find(string x)
    {
        if(p[x]==x) return x;
        else return p[x]=find(p[x]);
    }
    int main()
    {
        string sss,ss;
        char c;
        while(1)
        {
            scanf("%c",&c);
            if(c=='$')
                break;
            else if(c=='#')
            {
                cin>>ss;
                if(p[ss]=="")
                    p[ss]=ss;
            }
            else if(c=='?')
            {
                cin>>sss;
                cout<<sss<<" "<<find(sss)<<endl;
            }
            else if(c=='+')
            {
                cin>>sss;
                p[sss]=ss;
            }
        }
    }

    P1536 村村通

     找查两两村是否联通,即是否为同一个祖先,变量s作为记录连通块中的边数,因为并不是每个村和其他的村庄都是联通的,有可能存在鼓励的村庄。

    #include <bits/stdc++.h>
    using namespace std ;
    typedef long long ll;
    const ll maxn=2e5+10;;
    ll n,m,a[maxn];
    inline void pre()
    {
        for(ll i=1; i<=n; i++)
        {
            a[i]=i;
        }
    }
    inline ll find(ll k)
    {
        if(a[k]!=k) a[k]=find(a[k]);
        return a[k];
    
    }
    void merge(ll u,ll v)
    {
        a[find(u)]=find(v);
    }
    int main()
    {
        while(1)
        {
            ll f;
            scanf("%lld",&f);
            n=f;
            if(f==0) return 0;
            pre();
            scanf("%lld",&m);
            ll s=0;
            for(ll i=1; i<=m; i++)
            {
                ll x,y;
                scanf("%lld%lld",&x,&y);
                merge(x,y);
            }
            for(ll i=1; i<=n; i++)
            {
                if(a[i]==i)
                {
                    s++;
                }
            }
            cout<<s-1<<endl;
        }
    }

    P1396 营救

     创建结构体,存入u,v,w,对拥挤度w进行升序排序;

    并查集,当查询到s和t连通时,输出当前的最大拥挤度,由于之前已经有过升序排序的操作,这时候的最大拥挤度是最小的。

    #include <bits/stdc++.h>
    using namespace std ;
    typedef long long ll;
    const ll maxn=2e5+10;
    struct node
    {
        ll u,v,w;
    }way[maxn];
    inline bool cmp(node a,node b)
    {
        return a.w<b.w;
    }
    ll n,m,s,t;
    ll a[maxn];
    void pre()
    {
        for(ll i=0;i<maxn;i++)
        {
            a[i]=i;
        }
    }
    inline ll find(ll k)
    {
        if(a[k]!=k)
        {
            a[k]=find(a[k]);
        }
        return a[k];
    }
    inline void merge(ll uu,ll vv)
    {
        ll uuu=find(uu),vvv=find(vv);
        if(a[uuu]!=vvv)
            a[uuu]=vvv;
    }
    int main()
    {
        scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
        pre();
        for(ll i=1;i<=m;i++)
        {
            scanf("%lld%lld%lld",&way[i].u,&way[i].v,&way[i].w);
        }
        sort(way+1,way+1+m,cmp);
        for(ll i=1;i<=m;i++)
        {
            merge(way[i].u,way[i].v);
            if(find(a[s])==find(a[t]))
            {
                printf("%lld
    ",way[i].w);
                break;
            }
        }
    }

    P1621 集合

    P4185 [USACO18JAN]MooTube

    P1197 [JSOI2008]星球大战

    bzoj2054疯狂的馒头

    P2294 [HNOI2005]狡猾的商人

    P1892 [BOI2003]团伙

    Interesting Computer Game   2020牛客暑期多校训练营(第八场)

    #include <bits/stdc++.h>
    #define T int t ;cin >> t;while(t--)
    using namespace std ;
    typedef long long ll;
    const int maxn = 2e5 + 10;
    ll vis[maxn],a[maxn],b[maxn],c[maxn],pre[maxn];
    //vis数组表示的是当前的节点是否被访问过
    //pre数组表示的是合并路径
    inline ll find(ll x)
    {
        return (x==pre[x]) ? x:pre[x]=find(pre[x]);
    }
    inline void merge(ll u,ll v)
    {
        ll x=find(u),y=find(v);
        if(x==y)
        {
            vis[x]=1;
            return;
        }
        pre[x]=y;//表示x,y的祖宗合并,即两者为同一祖先
        if(vis[x])vis[y]=1;//该节点表示已被访问
    }
    int main()
    {
        ll total=0;//用于输出Case情况的个数
        T
        {
            total++;
            ll n;
            ll tot=0;//tot表示存入数的个数
            scanf("%lld",&n);
            for(ll i=1; i<=n; i++)
            {
                scanf("%lld%lld",&a[i],&b[i]);
                c[++tot]=a[i];//c数组用于存放每个数字,方便接下去排序和去重,假设a和b的值完全不一样,那么c数组的大小需要两倍空间,即2e5+5
                c[++tot]=b[i];
            }
            for(ll i=0; i<=maxn; i++)//初始化操作
            {
                vis[i]=0;
                pre[i]=i;
            }
            sort(c+1,c+tot+1);//升序排序,便于接下来的去重
            int cnt=unique(c+1,c+tot+1)-(c+1);//去重,方便编号
            for(ll i=1; i<=n; i++)
            {
                a[i]=lower_bound(c+1,c+tot+1,a[i])-c;
    //从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,
    //找到返回该数字的地址,不存在则返回end。
    //通过返回的地址减去起始地址begin,得到找到数字在数组中的下标,下标即树的度数
                b[i]=lower_bound(c+1,c+tot+1,b[i])-c;
                merge(a[i],b[i]);//用下标代替实际数字,防止数组存不下
            }
            int ans=tot;
            for(ll i=1; i<=tot; i++)
            {
                if(pre[i]==i&&!vis[i])ans--;//根据《离散数学》相关知识,如果非连通块则ans减一
            }
            cout<<"Case #"<<total<<": "<<ans<<endl;
        }
    }

     P3958 奶酪

     被scy修改过的删边问题

    贪心题

    只要保证连通,即只要从树根节点到达叶子节点,变成初级通路(每个点只经过一次,边数=点数-1),不必构成回路,结果等于原始总边数(m)-初级通路(n-1)

    #include <bits/stdc++.h>
    using namespace std ;
    int main()
    {
        int a,b;
        cin>>a>>b;
        int bb=b;
        while(b--){int x;cin>>x>>x;}
        printf("%d
    ",bb-a+1);
    }

     家族

    查找连通块的个数,如果a[i]==i,则s++,s变量统计的就是连通块的个数。

    #include<bits/stdc++.h>
    typedef long long ll;
    using namespace std;
    const ll maxn=1e5+10;
    ll a[maxn];
    void pre()
    {
        for(ll i=1;i<maxn;i++)
        {
            a[i]=i;
        }
    }
    inline ll find(ll k)
    {
        if(a[k]==k)
        {
            return a[k];
        }
        else
        {
            return a[k]=find(a[k]);
        }
    }
    inline void merge(ll u,ll v)
    {
        a[find(u)]=find(v);
    }
    ll n,m;
    int main()
    {
        scanf("%lld%lld",&n,&m);
        pre();
        ll x,y;
        for(ll i=1;i<=m;i++)
        {
            scanf("%lld%lld",&x,&y);
            merge(x,y);
        }
        ll s=0;
        for(ll i=1;i<=n;i++)
        {
            if(a[i]==i)
                s++;
        }
        cout<<s<<endl;
    }
  • 相关阅读:
    python基础day3-今日内容,2019-6-25
    python基础day3-视频下载,2019-6-25
    append,extend,insert的区别
    DOM基础之获取元素
    p1553数组反转
    python函数
    python文件处理
    day02 python基础之列表,元祖,字典
    day01 python基础
    Python绘图Turtle库详解
  • 原文地址:https://www.cnblogs.com/jackwang-sparrow/p/13419527.html
Copyright © 2011-2022 走看看