树- 并查集

来源:互联网 发布:erp软件二次开发 编辑:程序博客网 时间:2024/06/08 11:36

POJ 2524 宗教信仰

#include <bits/stdc++.h>using namespace std;const int N = 5e4 + 8;int m, n, fx, fy, x ,y,cnt,tot;int f[N], nn[N];int find(int x){    return (x == f[x] ? f[x] : f[x] = find(f[x]));}void Union(int x, int y){    fx = find(x);    fy = find(y);    if(fx != fy) f[fx] = fy;}int main(){#ifndef ONLINE_JUDGE    freopen("input.txt","r",stdin);    freopen("output.txt","w",stdout);#endif    scanf("%d%d",&n,&m);    while(m != 0 && n != 0)    {        cnt = 0;        for(int i = 1; i <= n; i++) f[i] = i;        memset(nn,0, sizeof(nn));        for(int i = 1; i <= m; i++)        {            scanf("%d%d",&x,&y);            Union(x,y);        }        printf("Case %d: ",++tot);        for(int i = 1; i <= n; i++)        {            //printf("%d %d\n",i,f[i]);            nn[find(i)]++;        }        for(int i = 1; i <= n; i++)        {            if(nn[i] != 0) cnt ++;        }        printf("%d\n",cnt);        scanf("%d%d",&n,&m);    }#ifndef ONLINE_JUDGE    fclose(stdin); fclose(stdout);#endif}

裸的并查集 注意初始化即可。

POJ 1703 发现它 抓住它

描述
一个城市中有两个犯罪团伙A和B,你需要帮助警察判断任意两起案件是否是同一个犯罪团伙所为,警察所获得的信息是有限的。假设现在有N起案件(N<=100000),编号为1到N,每起案件由团伙A或团伙B所为。你将按时间顺序获得M条信息(M<=100000),这些信息分为两类:
1、 D [a] [b]
其中[a]和[b]表示两起案件的编号,这条信息表明它们属于不同的团伙所为

2、A [a] [b]
其中[a]和[b]表示两起案件的编号,这条信息需要你回答[a]和[b]是否是同一个团伙所为
注意你获得信息的时间是有先后顺序的,在回答的时候只能根据已经接收到的信息做出判断。

输入
第一行是测试数据的数量T(1<=T<=20)。
每组测试数据的第一行包括两个数N和M,分别表示案件的数量和信息的数量,其后M行表示按时间顺序收到的M条信息。

输出
对于每条需要回答的信息,你需要输出一行答案。如果是同一个团伙所为,回答”In the same gang.”,如果不是,回答”In different gangs.”,如果不确定,回答”Not sure yet.”。

样例输入
1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4
样例输出
Not sure yet.
In different gangs.
In the same gang.

解题思路

利用保存了与父节点关系的并查集进行相应的操作
如果并不在一个并查集中说明现在他们之间的关系还不能确定
关注关系的维护
在每一次进行find操作的时候 由于我们会把f[x]更改为find(f[x]) 所以我们确定rel[x]的时候一定要先对f[x]执行find操作

#include <iostream>#include <string.h>using namespace std;int  x,y,fx, fy,n,M;int T;const int N = 1e5 + 5;int f[N], rel[N];int find(int x){    if( x == f[x] ) return x;    else{        int temp = find(f[x]);        rel[x] = (rel[f[x]] + rel[x]) % 2;        f[x] = temp;//一定要注意顺序的问题 一定要先对f[x]进行find操作 这样才能正确得到rel[f[x]]的值    }    return f[x];}void Union(int x, int y){    fx = find(x);    fy = find(y);    if(fx != fy)    {        f[fx] = fy;        if(rel[y] == 0)            rel[fx] = 1 - rel[x];        else             rel[fx] = rel[x];    }}int main(){#ifndef ONLINE_JUDGE    freopen("input.txt", "r", stdin);    freopen("output.txt", "w", stdout);#endifscanf("%d",& T);while(T--){    scanf("%d%d",&n, &M);    char ch[3];    for(int i = 1; i <= n; i++)f[i] = i, rel[i] = 0;    for(int i = 1; i <= M; i++)    {        scanf("%s",ch);        scanf("%d%d",&x,&y);        if(ch[0] == 'A')         {            // printf("%d %d %d %d",x,y,find(x),find(y));            if(find(x) != find(y))            printf("Not sure yet.\n");            else            {                if(rel[x] == rel[y])                printf("In the same gang.\n");                else                 printf("In different gangs.\n");            }        }        else        {            Union(x,y);        }    }}#ifndef ONLINE_JUDGE    fclose(stdin); fclose(stdout);#endif    return 0;}

注意点:
一个是rel的维护
一个是初始化
一个是关于char输入时最好用ch[3]来避免读入换行等情况。

关于关系的维护 另一道类似的题目

POJ-1182 食物链

在考虑关系的时候容易出错,事实上,我们可以从向量的角度来考虑这个问题
注意点: 在查找的关系中,与其父节点的关系会发生变化 因为会实现路径压缩

rootx->rooty = rootx->x + x->y + y->rooty

完整代码实现

#include <iostream>using namespace std;const int Max = 5e4 + 8;int N, K;int D, x, y, fx, fy;int cnt;int f[Max], rel[Max];//f用来存放父亲(祖先) rel用来存放关系 如果是同类就存0 如果是x 吃f[x]存1  f[x] 吃x2int find(int x){    if(f[x] == x) return x;    int temp = 0;    temp = find(f[x]);    rel[x] = (rel[x] + rel[f[x]]) % 3;    return f[x] = temp;}void Union(int x, int y, int D){    fx = find(x);    fy = find(y);    f[fx] = fy;    rel[fx] = (3 - rel[x] + rel[y] + D - 1) % 3;}int main(){#ifndef ONLINE_JUDGE    freopen("input.txt","r",stdin);    freopen("output.txt","w",stdout);#endif    scanf("%d%d",&N,&K);    for(int i = 1; i <= N; i++) f[i] = i, rel[i] = 0;    for(int i = 1; i <= K; i++)    {        //printf("N%d K%d\n",N,K);        scanf("%d%d%d",&D,&x,&y);        //printf("%d,%d,%d\n",D,x,y);        if(x > N || y > N || (x == y && D == 2)) {cnt++; continue;}        fx = find(x), fy = find(y);        //-66666666printf("x:%d,y:%d,fx:%d fy:%d\n",x,y,fx,fy);        if(fx == fy)        {            //printf("x:%d,y:%d,fx:%d,fy:%d,relx:%d,rely%d\n",x,y,fx,fy,rel[x],rel[y]);            if((D== 1 && rel[x] != rel[y]) || (D == 2 && (rel[x] - rel[y]) % 3 != 1))            {                // bug出现在这里  这里还是用向量的思想---- x->y = x->root + root->y  !                 cnt++;            }            continue;        }        else Union(x,y,D);        //printf("x:%d,y:%d,fx:%d,fy:%d,relx:%d,rely%d\n",x,y,fx,fy,rel[x],rel[y]);    }    printf("%d\n",cnt);#ifndef ONLINE_JUDGE    fclose(stdin); fclose(stdout);#endif}

总结

  • 两种并查集
    • 给出某两者属于同一并查集 只需要实现简单的维护和查询即可
    • 带关系的并查集, 在同一个并查集中仅仅意味着两者关系可知
  • 注意点
    • 初始化
    • 带关系的并查集
      • 在路径压缩的过程中先对父节点进行find 再更新rel 否则得到的rel不是与根节点的关系
      • 关于关心的更新和维护可以参考向量的概念!(以及记得取余数)
      • 具体介绍(http://blog.csdn.net/niushuai666/article/details/6981689)

带关系的并查集的另一种解法:以食物链为例

维护3个并查集
http://blog.csdn.net/backforward/article/details/51892505
很神奇的想法了~

原创粉丝点击