HDU 2473 Junk-Mail Filter 并查集

http://acm.hdu.edu.cn/showproblem.php?pid=2473

题意:

  N 是 标号为 0~(N-1)的邮件 M是有M行数据。

  第二行开始为数据   当输入为M时之后跟着的两个编号表示这两封邮件都为一种垃圾邮件。

当输入为S时跟着的一个标号表示这封邮件被误判,这不是封垃圾邮件,而之前与这封邮件同时被判为垃圾邮件的那封邮件还是垃圾邮件。

输出为有多少种邮件(垃圾邮件也分很多种)。

 

坑爹:

  1.当要将一封垃圾邮件变为普通邮件时,如果在这个垃圾邮件的树中作为根的话,那么变为普通邮件时要将剩余的垃圾邮件重新合并起来。

  2.当要搜索有多少个集合(邮件种类)的时候要查找   father[i] == i   这种有多少个,但如果是 (0-1-2-3-4)(假设根为0),将01234这些点都删除了,

用 father[i] == i 来找集合的话会把前面的  (0邮件)也会算上的。

 

解法:

  用一个代理的数组(起始跟输入的值一样),访问、删除的时候将real数组的值改变就行了(删除就将real[i]等于代理数组下标为n以后的位置),而合并就用代理数

组进行操作就行了。

  
 

View Code
  1 #include<iostream>
  2 using namespace std;
  3 
  4 const int maxn = 1000000 + 100000 +10;
  5 
  6 int real[maxn];
  7 int father[maxn];  //father[x]表示x的父节点
  8 int rank[maxn];     //rank[x]  秩,表示x节点所在树的深度
  9 int save[maxn];
 10 int used[maxn];
 11 
 12 void Make_Set()
 13 {
 14     memset(used,0,sizeof(used));
 15     for(int i=0;i<maxn;i++)
 16     {
 17         father[i]=i;    //初始化一开始每个节点的父节点都为本身
 18         rank[i]=1;        //初始化一开始每棵树的深度为1
 19     }
 20 }    
 21 
 22 int findroot (int a )// 寻找x元素所在的集合也就是找子节点的根节点
 23 {
 24     int k=0;
 25     while ( a != father[a] )
 26     {
 27         save[k++] = a;
 28         a = father[a];
 29     }
 30     for(int i=0;i<k;i++)
 31         father[ save[i] ] = a;    
 32     return a;
 33 }
 34 
 35 
 36 void Union(int x,int y)    //合并两个不相交的集合,x,y分别为两个不同的集合
 37 {
 38     x = findroot(x);
 39     y = findroot(y);
 40     if(x != y) 
 41     {
 42         if(rank[x] > rank[y])   //如果x树的深度比y树深,y树接到x树
 43         {
 44             father[y] = x;
 45         }
 46         else if(rank[x] < rank[y])
 47         {
 48             father[x] = y;
 49         }
 50         else if(rank[x] ==rank[y])  //若两树的深度一样
 51         {
 52             father[x] = y;           //则x树接到y树
 53             rank[y]++;              //此时y树的深度+1
 54         }
 55     }
 56 }
 57 
 58 
 59 int main()
 60 {
 61     int N;
 62     int M;
 63     int k = 1;
 64     while(cin>>N>>M,N+M)
 65     {
 66         Make_Set();
 67         int i;
 68         for(i =0; i<N;i++)         // 让real 和 father 一一对应
 69         {
 70             real[i] = i;
 71         }
 72         int count = N - 1;           // 要删除结点时在father数组中的最后一位给real[del]一个新的代理
 73         while(M-- )
 74         {
 75             char ch;
 76             cin>>ch;
 77             if(ch == 'M')
 78             {
 79                 int x;
 80                 int y;
 81                 cin>>x>>y; 
 82                 Union(real[x],real[y]);
 83             }
 84             if(ch == 'S')
 85             {
 86                 int del;
 87                 cin>>del;
 88                 real[del] = ++count ;
 89             }
 90         }
 91         
 92         int rootcount = 0;
 93         //int max = N;
 94         /*if(count > N - 1)
 95         {
 96             max = count;
 97         }*/
 98         for(i =0; i<N; i++)
 99         {
100             int a=findroot(real[i]);
101             if(!used[a])
102             {
103                 //printf("%d***\n",a);
104                 used[a]=1;
105                 rootcount++;
106             }
107         }
108         cout<<"Case #"<<k++<<": "<<rootcount<<endl;
109     }
110     return 0;
111 }

 

 

posted @ 2012-09-03 19:37  pc....  阅读(250)  评论(0)    收藏  举报