zoukankan      html  css  js  c++  java
  • 带权值的并查集整理与练习题

    博文: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<<'
    ';
        }
    }
    不怕万人阻挡,只怕自己投降。
  • 相关阅读:
    redis发布订阅
    redis学习笔记(面试题)
    redis安全 (error) NOAUTH Authentication required
    HDU3001 Travelling —— 状压DP(三进制)
    POJ3616 Milking Time —— DP
    POJ3186 Treats for the Cows —— DP
    HDU1074 Doing Homework —— 状压DP
    POJ1661 Help Jimmy —— DP
    HDU1260 Tickets —— DP
    HDU1176 免费馅饼 —— DP
  • 原文地址:https://www.cnblogs.com/newstartCY/p/11601164.html
Copyright © 2011-2022 走看看