• 带权值的并查集整理与练习题


    博文:https://blog.csdn.net/yjr3426619/article/details/82315133

    带全并查集

    路径压缩,表达每个当前node与 当前node所在的并查集的root 之间的关系

    并查集一定是单向连通的,所以一些node处理时 【node1,node2】 weight 可能需要把 一端的闭区间变为开区间

    俩个相对信息求出绝对信息   --水题

    例题 : HDU 3038 

    //带权值并查集
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn = 2e5 + 25;
    int dis[maxn];
    int p[maxn];
    int find(int u)//寻根
    {
        if(u!=p[u])
        {
            int tmp = p[u];//保存原来的根节点
            p[u] = find(p[u]);//路径压缩
            dis[u] += dis[tmp];//dis[tmp] 已经为tmp(即原来父节点)到根节点的距离所以加上本身自身到tmp的dis则为u到root的dis  
        }
        return p[u];
    }
    void merge(int u,int v,int value)
    {
        int f1 = find(u);
        int f2 = find(v);
        p[f1] = f2;
        dis[f1] = value + dis[v] - dis[u];
    }//带权并查集合并
    int main()
    {
        int n,m;//n节点,m条边
        while(cin>>n>>m)
        {
            int ans = 0,u,v,value;
            for(int i=0;i<=n;++i)
            {
                dis[i] = 0;
                p[i] = i;
            }
            while(m--)
            {//图改成了从0开始(important)
                cin>>u>>v>>value;
                --u;
                if(find(u)==find(v))
                {
                    if((dis[u]-dis[v])!=value)
                        ++ans;
                }else{
                    merge(u,v,value);//否则合并
                }
            }
            cout<<ans<<endl;
        }
    }

     hihoCoder 1515

    //带权并查集
    #include<iostream>
    #include<cstdio>//不是同一集合,合并,否则不做操作
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int maxn = 1e5 + 15;
    int dis[maxn],p[maxn];//当前node到root的距离
    int find(int u)
    {
        if(u!=p[u])
        {
            int tmp = p[u];
            p[u] = find(p[u]);
            dis[u] += dis[tmp];
        }
        return p[u];
    }
    int main()
    {
        int n,m,q;//n个节点,m条边,q个查询
        while(cin>>n>>m>>q)
        {
            for(int v=1;v<=n;++v)
            {
                dis[v] = 0;
                p[v] = v;
            }
            int u,v,value;
            while(m--)
            {
                cin>>u>>v>>value;
                int f1 = find(u);
                int f2 = find(v);
                if(f1!=f2)
                {
                    p[f1] = f2;
                    dis[f1] = value + dis[v] - dis[u];
                }
            }
            while(q--)
            {
                cin>>u>>v;
                if(find(u)!=find(v))
                    cout<<-1<<endl;
                else
                    cout<<dis[u] - dis[v]<<endl;
            }
        }
    }  
    

    POJ 2492

    //带权并查集(主要用于逻辑判断)      //题量还是少了,思维不够...(真菜)
    #include<iostream>//边的权值代表
    #include<cstdio>
    #include<cstring>//其实就是构建出了一符图,每一个集合内的node都与这个图的root有一个关系,
    //通过中间关系root判断条件是否矛盾
    #include<algorithm>//POJ 2492 
    using namespace std;
    const int maxn = 2e3 + 20;
    int p[maxn],dis[maxn];
    int find(int u)
    {
        if(p[u]!=u)
        {
            int tmp = p[u];//保存原来父节点
            p[u] = find(p[u]);
            dis[u] = (dis[u] + dis[tmp]) % 2;
        }
        return p[u];
    }
    int main()
    {
        int kase = 0,T,n,m,u,v;
        scanf("%d",&T);
        while(T--)
        {
            if(kase)
                printf("
    ");
            printf("Scenario #%d:
    ",++kase);
            scanf("%d%d",&n,&m);//n个节点,m个异性
            for(int v=1;v<=n;++v)
            {
                p[v] = v;
                dis[v] = 0;
            }
            bool flag = false;
            while(m--)
            {
                scanf("%d%d",&u,&v);
                if(flag)
                    continue;
                int f1 = find(u);
                int f2 = find(v);
                if(f1==f2)
                {
                    if(dis[u]==dis[v])
                        flag = true;//u ~ v 节点为相同性别
                }else{  
                    p[f1] = f2;
                    dis[f1] = (1 + dis[v] - dis[u]) % 2;
                }
            }
            if(flag)
                printf("Suspicious bugs found!
    ");
            else
                printf("No suspicious bugs found!
    ");
        }
    }

     POJ 1128

    思路还是很简单的,权值为0为同类,1 为 A 吃 B,2 为 A B 被 B 吃

    简单推下关系就好了

    //#include<bits/stdc++.h>//带权并查集(处理相对问题)
    //#include<array>
    #include<iostream>//加了个关闭输入流,一直WA,感受到了测评姬深深恶意
    #include<cstdio>
    #define inf (0x3f3f3f3f)//状态的选择一定是在merage时,so 不存在一条边既表示吃又表示被吃的关系,题量少了,入了坑...
    using namespace std;//POJ1128 食物链
    const int maxn = 5e4 + 15;
    //array<int,maxn> p;
    //array<int,maxn> dis;
    int p[maxn],dis[maxn];
    int find(int node)
    {
        if(node!=p[node])
        {
            int tmp = p[node];
            p[node] = find(p[node]);
            dis[node] = (dis[tmp] + dis[node]) % 3;
        }
        return p[node];
    }
    int main()
    {
        int n,k;
        int cmd,u,v;
        cin>>n>>k;
        int ans = 0;
        for(int i=1;i<=n;++i)
        {
            p[i] = i;
            dis[i] = 0;
        }
        while(k--)
        {
            scanf("%d%d%d",&cmd,&u,&v);
            if(u>n||v>n||(cmd==2&&u==v))
            {
                ++ans;
                continue;
            }
            int f1 = find(u);
            int f2 = find(v);
            if(f1!=f2)
            {
                p[f1] = f2;
                dis[f1] = (cmd - 1 + dis[v] - dis[u]) % 3; 
            }else{
                if(cmd-1!=(dis[u]-dis[v]+3)%3)
                    ++ans;
            }   
        }
        cout<<ans<<endl;
        return 0;
    }

     POJ 2912 (逆向思维真重要)

    #include<iostream>//带权并查集
    #include<cstdio>
    #include<algorithm>
    using namespace std;//(important) 如何找到最先能确定裁判的位置(想了很久,真菜)
    // 因为是每次枚举去尝试n个人,哪个人为裁判,如果选中了一个裁判编号,则证明其他人都不是裁判,所以其余n-1个人的枚举出现矛盾
    // so 剩余的n - 1个出现的矛盾行数最晚的则为最早能确定选定的是裁判的行数
    int n,m;
    const int maxn = 512;
    const int maxx = 2e3 + 48;
    int p[maxn],dis[maxn],node1[maxx],node2[maxx];
    char Cmp[maxx];
    int find(int u)
    {
        if(p[u]!=u)
        {
            int parent = p[u];
            p[u] = find(p[u]);
            dis[u] = (dis[u] + dis[parent]) % 3;
        }
        return p[u];
    }
    int main()
    {
        int u,v;
        char cmp;
        while(scanf("%d%d",&n,&m)==2)
        {// 0 表示等于 1 表示大于 2 表示小于
            for(int i=0;i!=m;++i)
                scanf("%d%c%d",&node1[i],&Cmp[i],&node2[i]);
            int cnt = 0,pos = 0,person = -1;//可能为裁判的人 最近能判断
            int i,j;
            for(i = 0;i!=n;++i)//枚举每一个人
            {
                for(j=0;j!=n;++j)
                {
                    p[j] = j;//森林
                    dis[j] = 0;
                }//重置
                for(j = 0;j!=m;++j)
                {
                    if(node1[j]==i||node2[j]==i)
                        continue;
                    int value;
                    if(Cmp[j]=='=')
                        value = 0;
                    else if(Cmp[j]=='>')
                        value = 1;
                    else
                        value = 2;
                    //cout<<node1[j]<<" "<<node2[j]<<endl;
                    int f1 = find(node1[j]);
                    int f2 = find(node2[j]);
                    //cout<<f1<<" "<<f2<<endl;
                    if(f1 != f2)//如果不存在关系
                    {
                        p[f1] = f2;
                        dis[f1] = ( dis[node2[j]] + value - dis[node1[j]] + 3) % 3; 
                    }else{
                        if(value != ( dis[node1[j]] - dis[node2[j]] + 3) % 3)
                        {//起冲突
                            pos  = max(pos,j+1);
                            break;
                        }
                    }
                }
                if(j==m)
                {
                    ++cnt;
                    person = i;
                }
            }
            if(!cnt)
                printf("Impossible
    ");
            else if(cnt>1)
                printf("Can not determine
    ");
            else{
                printf("Player %d can be determined to be the judge after %d lines
    ",person,pos);
            }
        }
    }
    

      POJ1456 水题,不知道为什么归为并查集....

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int maxn  = 1e4 + 32;
    typedef long long i64;
    int n;
    typedef struct{
        int value,weight;
    }node;
    bool cmp(const node& n1,const node& n2)
    {
        if(n1.value!=n2.value)
            return n1.value > n2.value;
        return n1.weight > n2.weight;
    }
    bool vis[maxn];
    vector<node> v;
    int main()
    {
        ios::sync_with_stdio(false);    cin.tie(0),cout.tie(0);
        while(cin>>n)
        {
            v.clear();
            memset(vis,false,sizeof(vis));
            node tmp;
            for(int i=0;i!=n;++i)
            {
                cin>>tmp.value>>tmp.weight;
                v.push_back(tmp);
            }
            sort(v.begin(),v.end(),cmp);
            i64 sum = 0;
            for(int i=0;i!=n;++i)
            {
                int pos = 0;
                for(int j=v[i].weight;j>=1;--j)
                {
                    if(!vis[j])
                    {
                        vis[j] = true;
                        pos = j;
                        break;
                    }
                }
                if(pos!=0)
                    sum += v[i].value;
            }
            cout<<sum<<'
    ';
        }
    }
    

      

     POJ 1984 https://vjudge.net/problem/POJ-1984

    //一道比较有意思的题目,刚开始没管方向,带权并查集胡乱一搞

    思路:因为求的是 节点 x 和 y 的 曼哈顿距离,所以单纯只计算节点之间距离会有问题,会出现样例中的情况,即可能存在更小的距离

    所以要分为 disx 和 disy 俩个方向,它们表示的是与并查集中的根的相对关系(即可能为正也可能为负,正负表示方向,分别带权并查集计算与跟节点的距离就好了)

    //#include<bits/stdc++.h>//带权并查集
    #include<vector>
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int maxn = 4e4 + 32;
    int p[maxn],disx[maxn],disy[maxn];//分别计算根节点
    typedef long long i64;
    int n,m,q;
    typedef struct{
        int u,v,value;
        char pos;
    }node;
    typedef struct{
        int u,v,time,index;//index 查询的位值
    }answer;
    typedef struct{
        int len,index;
    }nodeEnd;
    bool cmp(const answer& a1,const answer& a2)
    {
        return a1.time < a2.time;
    }
    bool cmp2(const nodeEnd& e1,const nodeEnd& e2)
    {
        return e1.index < e2.index;
    }
    vector<node> v;
    vector<answer> an;
    vector<nodeEnd> ne;
    int find(int u)
    {
        if(p[u]!=u)
        {
            int parent = p[u];
            p[u] = find(p[u]);
            disx[u] += disx[parent];
            disy[u] += disy[parent];
        }
        return p[u];
    }
    int main()
    {
        ios::sync_with_stdio(false); cin.tie(0),cout.tie(0);
        while(cin>>n>>m)
        {
            v.clear();
            an.clear();
            ne.clear();
            for(int i=1;i<=n;++i)
            {
                p[i] = i;
                disx[i] = disy[i] = 0;
            }//init()
            node tmp;
            for(int i=0;i!=m;++i)
            {
                cin>>tmp.u>>tmp.v>>tmp.value>>tmp.pos;
                v.push_back(tmp);
            }
            answer tmpa;
            cin>>q;
            for(int i=0;i!=q;++i)
            {
                cin>>tmpa.u>>tmpa.v>>tmpa.time;
                tmpa.index = i;
                an.push_back(tmpa);
            }        
            sort(an.begin(),an.end(),cmp);
            int cnt = 0;//标签
            for(int i=0;i!=m;++i)
            {
                int f1 = find(v[i].u);
                int f2 = find(v[i].v);
                if(f1 != f2)
                {
                    p[f1] = f2;
                    if(v[i].pos=='N')
                    {
                        disy[f1] = v[i].value + disy[v[i].v] - disy[v[i].u];
                        disx[f1] = disx[v[i].v] - disx[v[i].u];//表达的是相对位置
                    }else
                    if(v[i].pos=='S')
                    {
                        disy[f1] = -v[i].value + disy[v[i].v] - disy[v[i].u];
                        disx[f1] = disx[v[i].v] - disx[v[i].u];
                    }
                    if(v[i].pos=='E')
                    {
                        disx[f1] = v[i].value + disx[v[i].v] - disx[v[i].u];
                        disy[f1] = disy[v[i].v] - disy[v[i].u];
                    }else
                    if(v[i].pos=='W')
                    {
                        disx[f1] = -v[i].value + disx[v[i].v] - disx[v[i].u];
                        disy[f1] = disy[v[i].v] - disy[v[i].u];
                    }
                }//匹配
                while(i+1==an[cnt].time)//当时间正好匹配
                {
                    nodeEnd tmp;
                    tmp.index = an[cnt].index;//下标
                    f1 = find(an[cnt].u);
                    f2 = find(an[cnt].v);
                    if(f1 != f2)
                    {
                        tmp.len = -1;
                    }else{
                        tmp.len = abs(disx[an[cnt].u] - disx[an[cnt].v]) + 
                        abs(disy[an[cnt].u] - disy[an[cnt].v]);
                    }
                    ne.push_back(tmp);
                    ++cnt; 
                }
            }
            sort(ne.begin(),ne.end(),cmp2);
            for(int i=0;i!=ne.size();++i)
                cout<<ne[i].len<<'
    ';
        }
    }
    不怕万人阻挡,只怕自己投降。
  • 相关阅读:
    Recon-Erlang线上系统诊断工具
    erlang pool模块。
    深度学习Bible学习笔记:第二、三章 线性代数 概率与信息论
    LeetCode(5):最长回文子串
    LeetCode(4):两个排序数组的中位数
    LeetCode(3):无重复字符的最长子串
    LeetCode(2):Add Two Numbers 两数相加
    LeetCode(1):两数之和
    深度学习Bible学习笔记:第一章 前言
    2018年3月18日论文阅读
  • 原文地址:https://www.cnblogs.com/newstartCY/p/11601164.html
Copyright © 2020-2023  润新知