HDU ACM 2473 Junk-Mail Filter (并查集)
http://acm.hdu.edu.cn/showproblem.php?pid=2473
题意:输入 M 数字1 数字2 表示数字1和数字2是同一个集合.
S 数字1 表示数字1需要从集合中删除.求有几个集合
用并查集实现.
普通的并查集并不能实现删除的功能.
所以需要使用 "代理".
为了好理解先用两个数组.
5 6
M 0 1
M 1 2
M 1 3
S 1
M 1 2
S 3
未进行删除前.
进行删除后
删除节点并不是真的删除这个节点,而是把要删除的节点更换为另一个节点.
以后搜索他也是搜索另一个节点.
这样不会破坏并查集的结构又实现了删除.
在查找几个集合时要用used数组标记而不是判断 father[i] == i
若1为根节点,而1又被删除时则会出现错误.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 const int MAX = 100000 + 10; 6 int father[MAX*100]; 7 int rank1[MAX*100]; 8 int sign[MAX*100]; 9 10 void Make_Set(int x)//初始化 11 { 12 father[x] = x; 13 rank1[x] = 0; 14 } 15 16 int Find_Set(int x)//找根节点 17 { 18 int i=0; 19 while(father[x] != x) 20 { 21 sign[i++] = x; 22 x = father[x]; 23 } 24 for(i;i>=1;i--) 25 { 26 father[sign[i-1]] = x; 27 } 28 return x; 29 } 30 31 32 void Union(int x,int y)//合并 33 { 34 x = Find_Set(x); 35 y = Find_Set(y); 36 if(x == y) 37 { 38 return; 39 } 40 if(rank1[x] > rank1[y]) 41 { 42 father[y] = x; 43 } 44 else if(rank1[x] < rank1[y]) 45 { 46 father[x] = y; 47 } 48 else if(rank1[x] ==rank1[y]) 49 { 50 father[x] = y; 51 rank1[y]++; 52 } 53 } 54 55 //int num[MAX]; 56 int mark[MAX*100]; 57 bool used[MAX*100]; 58 int main() 59 { 60 int n,m; 61 int Case = 0; 62 while(cin>>n>>m,n+m) 63 { 64 Case++; 65 int i; 66 int mark_num = 0; 67 for(i=0;i<n;i++) 68 { 69 Make_Set(i); 70 mark[i] = i; 71 // num[i] = i; 72 } 73 memset(used,0,sizeof(used)); 74 while(m--) 75 { 76 char ch; 77 cin>>ch; 78 if(ch =='M') 79 { 80 int a,b; 81 cin>>a>>b; 82 Union(mark[a],mark[b]); 83 } 84 else 85 { 86 int x; 87 cin>>x; 88 mark[x] = mark_num + n; 89 mark_num++; 90 //mark[num[x]] = num[x]; 91 Make_Set(mark[x]); 92 } 93 } 94 int sum = 0; 95 for(i=0;i<n;i++) 96 { 97 if(!used[Find_Set(mark[i])]) 98 { 99 used[Find_Set(mark[i])] = 1; 100 sum++; 101 } 102 } 103 cout<<"Case #"<<Case<<": "; 104 cout<<sum<<endl; 105 } 106 return 0; 107 }