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

    A : Wireless Network  POJ - 2236 

    题意:并查集,可以有查询和修复操作

    题解:并查集

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<algorithm>
    using namespace std;
    #define N 1110
    int d;
    bool use[N];
     
    struct node
    {
        int pre;
        int x, y;
    }p[N];
     
    int find(int x)
    {
        return x == p[x].pre ? x : find(p[x].pre);
    }
     
    void join(const node p1, const node p2)
    {
        int root1, root2;
        root1 = find(p1.pre);
        root2 = find(p2.pre);
        if(root1 != root2)
            if((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) <= d * d)
                p[root2].pre = root1;
    }
     
    int main()
    {
        //freopen("Input.txt", "r", stdin);
        int num;
        char ope;
        int ok;
        int from, to;
        scanf("%d%d", &num, &d);
        for(int i = 1; i <= num; ++i)
            p[i].pre = i;
        memset(use, false, sizeof(use));
        for(int i = 1; i <= num; ++i)
            scanf("%d%d", &p[i].x, &p[i].y);
        while(scanf("
    %c", &ope) != EOF)
        {
            if(ope == 'O')
            {
                scanf("%d", &ok);
                use[ok] = true;
                for(int i = 1; i <= num; ++i)
                    if(use[i] && i != ok)
                        join(p[i], p[ok]);
            }
            else
            {
                scanf("%d%d", &from, &to);
                if(find(from) == find(to))
                    printf("SUCCESS
    ");
                else
                    printf("FAIL
    ");
            }
        }
        return 0;
    }
    View Code

    B:The Suspects POJ - 1611 

    题意:0节点为携带病毒人,有一些团队,一个人可以在多个团队,一个团队一个有病毒,其余都感染,问一共几人感染病毒

    题解:寻找0号根结点对应的sum值

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<sstream>
    #include<cmath>
    #include<stack>
    #include<cstdlib>
    #include <vector>
    #include<queue>
    using namespace std;
    
    #define ll long long
    #define llu unsigned long long
    #define INF 0x3f3f3f3f
    #define PI acos(-1.0)
    const int maxn =  1e5+5;
    const int  mod = 1e9+7;
    
    int father[maxn],sum[maxn];
    void init(int n)
    {
        for(int i=0;i<n;i++)
        {
            father[i] = i;
            sum[i] = 1;
        }
    }
    int find(int x)
    {
        if(father[x] == x)
            return x;
        else
            return father[x] = find(father[x]);
    }
    void combine(int x,int y)
    {
        int xx = find(x);
        int yy = find(y);
        if(xx != yy) {
            father[xx] = yy;
            sum[yy] += sum[xx];
        }
    }
    int main()
    {
        int n,m;
        while(~scanf("%d %d",&n,&m))
        {
            init(n);
            if(n == 0 && m == 0)
                break;
            for(int i=0;i<m;i++)
            {
                int x;
                scanf("%d",&x);
                int one,two;
                scanf("%d",&one);
                for(int j=1;j<x;j++)
                {
                    scanf("%d",&two);
                    combine(one,two);
                }
            }
            printf("%d
    ",sum[find(0)]);
        }
    }
    View Code

    C:How Many Tables  hdu-1213

    题意:认识的人可以坐在一张桌子,也可以间接认识,问需要几张桌子

    思路:并查集之后找有几个集

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<sstream>
    #include<cmath>
    #include<stack>
    #include<map>
    #include<cstdlib>
    #include <vector>
    #include<queue>
    using namespace std;
    const int INF = 0x3f3f3f3f;
    const int maxn = 1e3+5;
    
    int father[maxn];
    void init(int n)
    {
        for(int i=1;i<=n;i++)
            father[i] = i;
    }
    int find(int x)
    {
        if(father[x] == x)
            return x;
        else
            return father[x] = find(father[x]);
    }
    void combine(int x,int y)
    {
        x = find(x);
        y = find(y);
        if(x != y)
            father[x] = y;
    }
    int main()
    {
       int t;
       scanf("%d",&t);
       while(t--)
       {
           int n,m;
           scanf("%d %d", &n,&m);
           init(n);
           for(int i=0;i<m;i++)
           {
               int a,b;
               scanf("%d%d",&a,&b);
               combine(a,b);
           }
           int res = 0;
           for(int i=1;i<=n;i++)
               if(i == find(i))
                   res++;
           printf("%d
    ",res);
       }
    }
    View Code

    D:How Many Answers Are Wrong  hdu-3038

    题意:现在有n个数(你并不知道这n个数是什么),m次查询,每次查询给出u,v,w。表示从第u个数到第v个数的和为w。
       问,在这些查询中,有多少个是错误的(即有冲突)。

    思路:带权并查集

    从第u个数到第v个数的和其实可以理解为,第u-1个数到v个数之间的和。那么,就可以把和当成一种关系,利用带权并查集来维护这种关系。u-1节点为根,v的权值为第u-1个数到v个数之间的和。这里需要理解一下的是,在Find函数和unite(合并)函数里面,有两个关系域的转移方程(暂且这么叫他吧)
    ①p[x].relation+=p[tmp].relation
    ②p[root2].relation=p[x].relation+relation-p[y].relation

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<sstream>
    #include<cmath>
    #include<stack>
    #include<map>
    #include<cstdlib>
    #include <vector>
    #include<queue>
    using namespace std;
    const int INF = 0x3f3f3f3f;
    const int maxn = 2e5+5;
    struct node
    {
        int father;
        int w;
    };
    node p[maxn];
    int n,m,ans;
    void init()
    {
        ans = 0;
        for(int i=1;i<=n;i++)
        {
            p[i].father = i;
            p[i].w = 0;
        }
    }
    
    int find(int x)
    {
        if(x == p[x].father)
            return x;
       int tmp = p[x].father;
       p[x].father = find(tmp);
       p[x].w += p[tmp].w;
       return p[x].father;
    }
    
    void combine(int x,int y,int w)
    {
        int x1 = find(x);
        int y1 = find(y);
        if(x1 != y1)
        {
            p[y1].father = x1;
            p[y1].w = p[x].w + w -p[y].w;
        }
        else
        {
            if(p[y].w - p[x].w != w)
                ans ++;
        }
    }
    
    int main()
    {
        while(~scanf("%d %d",&n,&m))
        {
            int u,v,w;
            init();
            for(int i=0;i<m;i++)
            {
                scanf("%d %d %d",&u,&v,&w);
                u -= 1;
                combine(u,v,w);
            }
            printf("%d
    ",ans);
        }
    }
    View Code

    E - 食物链 POJ - 1182

    题意:A吃B,B吃C,C吃A,给出一些话,问有几句假话

    题解:可开3倍数组,也可以用结构体,关系域用向量加减操作

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<sstream>
    #include<cmath>
    #include<stack>
    #include<map>
    #include<cstdlib>
    #include<vector>
    #include<string>
    #include<queue>
    using namespace std;
    
    #define ll long long
    #define llu unsigned long long
    #define INF 0x3f3f3f3f
    const double PI = acos(-1.0);
    const int maxn =  5e4+10;
    const int mod = 1e9+7;
    
    struct node
    {
        int pre;
        int relation;  //0:与根节点同类 1:被根节点吃 2:吃根节点
    }p[maxn];
    
    int find(int x)
    {
        int temp;
        if(x == p[x].pre)
            return x;
        temp = p[x].pre;
        p[x].pre = find(temp);
        p[x].relation = (p[x].relation + p[temp].relation) % 3;
        return p[x].pre;
    }
    int main()
    {
        int n,k;
        int ope,a,b;
        int root1,root2;
        int sum = 0;
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)
        {
            p[i].pre = i;
            p[i].relation = 0;
        }
        while(k--)
        {
            scanf("%d%d%d",&ope,&a,&b);
            if(max(a,b) > n)
            {
                sum++;
                continue;
            }
            if(ope == 2 && a == b)
            {
                sum++;
                continue;
            }
            root1 = find(a);
            root2 = find(b);
            if(root1 != root2)
            {
                p[root2].pre = root1;
                p[root2].relation = (3+(ope - 1) + p[a].relation - p[b].relation) % 3;
            }
            else
            {
                if(ope == 1 &&p[a].relation != p[b].relation)
                {
                    sum++;
                    continue;
                }
                if(ope == 2 &&((3 - p[a].relation + p[b].relation) % 3 != ope -1))
                {
                    sum++;
                    continue;
                }
            }
        }
        printf("%d
    ",sum);
    }
    View Code

    F - True Liars POJ - 1417 

    题目大意:给你p1个好人和p2个坏人,编号为1-p1+p2,然后给你n中操作

                      x1 x2 no:x1说x2不是好人

                      x1 x2 yes:x1说x2是好人

                      在这里好人说的总是对的,坏人说的总是坏的,然后问你最后能不能唯一确定哪些是好人,并输出不能就输出no

    题目思路:开始看到这题很好想到的就是用并查集,如果是yes的话说明他们同类,no的话说明不是同类,但这样只能分出几个大集合来,每个集合又分成两个小集合表示两种类型的个数,而我们要求的是在所有大集合中选出一个小集合然后加起来看能不能组 合成p1,并且要唯一,这里我们可以想到用背包来做,dp[i][j]表示前i个大集合好人为j个的方案数,第i种状态只能是有第i-1种状态而来,我们用w0[i],w1[i]表示第i个集合两个小集合的个数 ,所以dp[i][j]可以由dp[i-1][j-w0[]i]和dp[i-1][j-w1[i]]得来,这样我们只需判断dp[cnt][p1]是否等于1,这题还有麻烦的就是输出好人的编号,这里我们可以利用边的权值,w0存的全是权值为0的w1存的全是权值为1的,然后最后只需判断下第i个集合是由w0还是w1组合而来,这里可以利用dp[i-1][p1-w0[i]]的值来判断,如果等于1则表示是由w0组合否则就是w1,这个不难理,dp[i-1][p1-w0[i]]==1可以得到dp[i][p1]==1,应为这里已经表示答案唯一,所以只有一种情况!

    此题题意题解转载自:   https://blog.csdn.net/qq_34731703/article/details/54603652

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<sstream>
    #include<cmath>
    #include<stack>
    #include<cstdlib>
    #include <vector>
    #include <set>
    #include<queue>
    using namespace std;
    
    #define ll long long
    #define llu unsigned long long
    #define INF 0x3f3f3f3f
    #define PI acos(-1.0)
    const int maxn =  1e3+5;
    const ll mod = 1e9+7;
    
    int pre[maxn],rk[maxn],p[maxn],vis[maxn];
    int w0[maxn],w1[maxn],dp[maxn][maxn];
    int n,p1,p2;
    void init()
    {
        for(int i=1;i<=p1+p2;i++)
        {
            pre[i] = i;
            rk[i] = 0;
            vis[i] = 0;
            w0[i] = w1[i] = 0;
        }
        memset(dp,0,sizeof dp);
    }
    int find(int x)
    {
        if(x != pre[x])
        {
            int temp = pre[x];
            pre[x] = find(pre[x]);
            rk[x] = rk[x] ^ rk[temp];
        }
        return pre[x];
    }
    void combine(int x,int y,int k)
    {
        int xx = find(x);
        int yy = find(y);
        if(xx != yy)
        {
            pre[xx] = yy;
            rk[xx] = rk[x] ^ rk[y] ^ k;
        }
    }
    int main()
    {
        while(scanf("%d %d %d",&n,&p1,&p2) && (n + p1 + p2))
        {
            init();
            for(int i=0;i<n;i++)
            {
                int a,b;
                char str[10];
                scanf("%d %d %s",&a,&b,str);
                if(str[0] == 'y')
                    combine(a,b,0);
                else
                    combine(a,b,1);
            }
            int cnt = 1;
            for(int i=1;i<=p1+p2;i++)
            {
                if(!vis[i])
                {
                    int fa = find(i);
                    for(int j=i;j<=p1+p2;j++)
                    {
                        if(find(j) == fa && !vis[j])
                        {
                            vis[j] = 1;
                            if(rk[j] == 0)
                                w0[cnt]++;
                            else
                                w1[cnt]++;
                        }
                    }
                    p[cnt] = fa;
                    cnt++;
                }
            }
            dp[0][0] = 1;
            for(int i=1;i<cnt;i++)
            {
                int minn = min(w0[i],w1[i]);
                for(int j=p1;j>=minn;j--)
                {
                    if(dp[i-1][j-w0[i]])
                        dp[i][j] += dp[i-1][j-w0[i]];
                    if(dp[i-1][j-w1[i]])
                        dp[i][j] += dp[i-1][j-w1[i]];
                }
            }
    
            if(dp[cnt-1][p1] != 1)
            {
                puts("no");
                continue;
            }
            int ans[maxn];
            int num = 0;
            int good = p1;
            for(int i=cnt-1;i>=1;i--)
            {
                if(dp[i-1][good-w0[i]] == 1)
                {
                    for(int j=1;j<=p1+p2;j++)
                        if(find(j) == p[i] && rk[j] == 0)
                            ans[num++]=j;
                    good -= w0[i];
                }
                else
                {
                    for(int j=1;j<=p1+p2;j++)
                        if(find(j) == p[i] && rk[j] == 1)
                            ans[num++] = j;
                    good -= w1[i];
                }
            }
            sort(ans,ans + num);
            for(int i=0;i<num;i++)
                printf("%d
    ",ans[i]);
            puts("end");
        }
        return 0;
    }
    View Code

    G - Supermarket POJ - 1456 

    题意:超市里有n个产品要卖,每个产品都有一个截至时间d(从开始卖时算起),只有在这个截至时间之前才能卖出并且获得率润dy。

    题解:先将产品按照价格从高到低排序,之后用并查集来寻找还有的时间

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<sstream>
    #include<cmath>
    #include<stack>
    #include<cstdlib>
    #include <vector>
    #include <set>
    #include<queue>
    using namespace std;
    
    #define ll long long
    #define llu unsigned long long
    #define INF 0x3f3f3f3f
    #define PI acos(-1.0)
    const int maxn =  1e4+5;
    const ll mod = 1e9+7;
    struct node
    {
        int p,d;
    }a[maxn];
    int pre[maxn];
    bool cmp(node a,node b)
    {
        return a.p > b.p;
    }
    int find(int x)
    {
        if(pre[x] == -1)
            return x;
        else
            return pre[x] = find(pre[x]);
    }
    int main()
    {
        int n;
        while(~scanf("%d",&n))
        {
            memset(pre,-1,sizeof pre);
            for (int i = 0; i < n; i++)
                scanf("%d %d",&a[i].p,&a[i].d);
            sort(a,a+n,cmp);
            int ans = 0;
            for(int i=0;i<n;i++)
            {
                int t = find(a[i].d);
               // cout<<t<<endl;
                if(t)
                {
                    ans += a[i].p;
                    pre[t] = t-1;
                    //cout<<"pre[t]"<<t-1<<endl;
                }
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code

     M - 小希的迷宫 HDU - 1272 

    题意:给一张地图,要求任意的两个房间之间只有一条路,是的输出Yes,不是的输出No

    思路:使用并查集,当发现两个房间的根节点不一样的话就将两个房间连接起来,如果两个房间的根节点是一样的话,说明成环了,就不是。同时,也有可能不是联通的,需要验证,就是只有一个根节点在所有的点中,还有一个特判,直接输入0 0 的话输出的是Yes

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<sstream>
    #include<cmath>
    #include<stack>
    #include<cstdlib>
    #include <vector>
    #include <set>
    #include<queue>
    #include<map>
    using namespace std;
    
    #define ll long long
    #define llu unsigned long long
    #define INF 0x3f3f3f3f
    #define PI acos(-1.0)
    const int maxn =  1e5+5;
    const ll mod = 1e9+7;
    const double eps = 1e-8;
    
    int pre[maxn];
    int vis[maxn];
    
    void init()
    {
        for(int i=1;i<maxn;i++)
            pre[i] = i;
        memset(vis,0,sizeof vis);
    }
    int find(int x)
    {
        if(x == pre[x])
            return x;
        else
            return pre[x] = find(pre[x]);
    }
    
    void combine(int x,int y)
    {
        int fx = find(x);
        int fy = find(y);
        if(fx != fy)
            pre[fy] = fx;
    }
    bool same(int x,int y)
    {
        int fx = find(x);
        int fy = find(y);
        return fx == fy;
    }
    int main()
    {
        int a,b;
        while(scanf("%d%d",&a,&b))
        {
            init();
            vis[a] = vis[b] = 1;
            if(a == -1 && b == -1)
                break;
            if(a==0 && b==0)
            {
                puts("Yes");
                continue;
            }
            combine(a,b);
            int x,y;
            int flag = 0;
            while(scanf("%d%d",&x,&y))
            {
                if(x==0 && y==0)
                {
                    int ans = 0;
                    for(int i=1;i<maxn;i++)
                        if(vis[i] && pre[i]==i) {
                            ans++;
                            //cout<<i<<endl;
                        }
                    if(flag == 0 && ans == 1)
                        puts("Yes");
                    else
                        puts("No");
                    break;
                }
                vis[x] = vis[y] = 1;
                if(!same(x,y))
                    combine(x,y);
                else
                    flag = 1;
            }
        }
    }
    View Code
  • 相关阅读:
    关于同余最短路
    【水】关于 __attribute__
    题解【AtCoder
    一些简单图论问题
    浅谈简单动态规划
    关于博客园主题(美化博客园)
    Algebra, Topology, Differential Calculus, and Optimization Theory For Computer Science and Machine Learning 第47章 读书笔记(待更新)
    Algebra, Topology, Differential Calculus, and Optimization Theory For Computer Science and Machine Learning 第46章 读书笔记(待更新)
    Algebra, Topology, Differential Calculus, and Optimization Theory For Computer Science and Machine Learning 第45章 读书笔记(待更新)
    Algebra, Topology, Differential Calculus, and Optimization Theory For Computer Science and Machine Learning 第44章 读书笔记(待更新)
  • 原文地址:https://www.cnblogs.com/smallhester/p/10316036.html
Copyright © 2011-2022 走看看