zoukankan      html  css  js  c++  java
  • POJ1703 Find them, Catch them [并查集]

    Find them, Catch them
    Time Limit: 1000MS   Memory Limit: 10000K
    Total Submissions: 21732   Accepted: 6461

    Description

    The police office in Tadu City decides to say ends to the chaos, as launch actions to root up the TWO gangs in the city, Gang Dragon and Gang Snake. However, the police first needs to identify which gang a criminal belongs to. The present question is, given two criminals; do they belong to a same clan? You must give your judgment based on incomplete information. (Since the gangsters are always acting secretly.)

    Assume N (N <= 10^5) criminals are currently in Tadu City, numbered from 1 to N. And of course, at least one of them belongs to Gang Dragon, and the same for Gang Snake. You will be given M (M <= 10^5) messages in sequence, which are in the following two kinds:

    1. D [a] [b]
    where [a] and [b] are the numbers of two criminals, and they belong to different gangs.

    2. A [a] [b]
    where [a] and [b] are the numbers of two criminals. This requires you to decide whether a and b belong to a same gang.

    Input

    The first line of the input contains a single integer T (1 <= T <= 20), the number of test cases. Then T cases follow. Each test case begins with a line with two integers N and M, followed by M lines each containing one message as described above.

    Output

    For each message "A [a] [b]" in each case, your program should give the judgment based on the information got before. The answers might be one of "In the same gang.", "In different gangs." and "Not sure yet."

    Sample Input

    1
    5 5
    A 1 2
    D 1 2
    A 1 2
    D 2 4
    A 1 4
    

    Sample Output

    Not sure yet.
    In different gangs.
    In the same gang.
    

    Source

     
     
     
     
     
     
     

    【转】

    解析:并查集的题目,并查集的拓展。一般的思路是先初始化,各个数自成一个组,然后是两个gangs自成一个组,但由于两个给定元素有三种关系: In the same gang;  In different gangs;  Not sure yet; 采用此模型的缺点是判断两个元素关系还未确定这种情况比较复杂,故模型需要改进。本题的正确模型是将已经确定关系的元素组成一个集合,然后利用两个元素的 father是同一个来确定这两个元素之间的关系。father[a]中存放的是a的根结点,rank中存放的是father[a]与a的关系,0表示两者不在同一个gangs中,1表示两者在同一个gangs中。具体的程序还是沿袭了并查集的Make_Set()、Find_Set()、 Union_Set()的三步骤。
     
     
    心得:并查集有三步是必须的:Make_Set()、Find_Set()、Union_Set()。  rank[a]的改变是伴随着father[a]的改变而更新的(有father改变就有rank改变),要是father改变了,而rank未改变,此时的rank就记录了一个错误的值,father未改变(即使实际的father已不是现在的值,但只要father未改变,rank 的值就是“正确”的,认识到这点很重要。),第一次错误就是因为没有考虑清楚这点。
     
     
    在前面一篇文章中讲的方法比较复杂,最近有学到一种更加通用的方法,因为它还适用于1182食物链中有三种集合相关联的情况,而且此法更为简洁。
    只需加一个rank数组,表示两种派别,值为0和1,初始化为0.
    查询时如果两人属于同一集合并且属于派别相同就输出In the same gang.否则输出In different gangs.不属于同一集合则不能判断。
    当我们要合并两个集合时,根据当前2人的派别可推出它们的祖先的派别应该怎样改变,见下图:


    若此时告诉5号和10号属于不同派别,则6的rank值应修改为1,即rank[0]=(rank[5]+rank[10]+1)%2;
    如果告诉4号和10号属于不同派别,则6的rank值应修改为0,即rank[6]=(rank[4]+rank[10]+1)%2;
     
    因为在合并时并没有把子树里的每个子节点都修改完,也没有那个必要,所以在find_set时要进行修改,很简单,儿子的值要与父亲的值不同,即rank[x]=(rank[x]+rank[p[x]])%2.
     
     
     
     
     
     
     
    code:
     1 #include<iostream>
     2 #include<cstdio>
     3 
     4 using namespace std;
     5 
     6 int father[100000+10];   //表示x的根结点 
     7 int rank[100000+10];
     8 
     9 
    10 void Make_Set(int n){
    11     int i;
    12     for(i=1;i<=n;i++){
    13        father[i] = i; 
    14        rank[i] = 1;                 
    15     }
    16 }
    17 
    18 int Find_Set(int a){
    19      if(a==father[a])  
    20         return a;
    21      else {
    22        int temp = father[a];
    23        father[a] = Find_Set(father[a]);   
    24        rank[a] = (rank[temp]+rank[a]+1)%2 ; 
    25   //必须有,更新路径压缩之后a与根结点之间的关系; father改变,rank就必须要跟着改变 
    26      } 
    27      return father[a];
    28 }
    29 
    30 void Union_Set(int a,int b){
    31      int fa,fb;
    32      fa = Find_Set(a);
    33      fb = Find_Set(b);
    34      if(fa!=fb){
    35          father[fa] = fb;   
    36          rank[fa] = (rank[a]+rank[b])%2 ;     //fa结点以下的结点的rank不需要改               
    37      }
    38 }
    39 
    40 int main(){
    41     //freopen("1in.txt","r",stdin);
    42     //freopen("1out.txt","w",stdout);
    43     int T,N,M;
    44     char ch;
    45     int a,b,fa,fb;
    46     scanf("%d",&T);
    47     while(T--){
    48        scanf("%d%d",&N,&M);
    49        Make_Set(N);
    50        int i;
    51        for(i=1;i<=M;i++){
    52            getchar();
    53            scanf("%c%d%d",&ch,&a,&b);
    54            if(ch=='A'){
    55                if(Find_Set(a)==Find_Set(b)){           
    56        //在使用rank之前,已经在用Find_Set函数寻找a的时候将a路径上的所有结点的rank值改变过了 
    57                    if((rank[a]+rank[b])%2==0){      
    58                       printf("In the same gang.\n");                           
    59                    }           
    60                    else
    61                       printf("In different gangs.\n");
    62                }  
    63                else{
    64                   printf("Not sure yet.\n");     
    65                }
    66                             
    67            }        
    68            else{
    69               Union_Set(a,b);
    70            }     
    71                          
    72        }  
    73                    
    74     }   
    75     return 0;
    76 }
     
     
     
     
     
  • 相关阅读:
    微信公众平台和微信开放平台的区别
    今天学习到的关于mysql数据库的linux命令
    myql --- mysqldump使用方法
    Git常用操作命令
    PHP中的日期加减方法示例
    php发送get、post请求的6种方法简明总结
    PHP时间日期比较
    Crystal Reports 2008(水晶报表) 安装
    Linux远程连接与常用命令
    Linux学习之CentOS(二十二)--单用户模式下修改Root用户的密码
  • 原文地址:https://www.cnblogs.com/XBWer/p/2626435.html
Copyright © 2011-2022 走看看