题目地址: http://poj.org/problem?id=1611
分析:
- 数据结构
- parent[x] 表示 x 元素的父节点位置.
- rank[x] 记录x的子链的长度, 以便在合并的时候减少链条长度. 查找的时候使用了路劲压缩, 所以两个节点的rank差不会大于1, 所以提高的效率也不是很大, 但还是很有帮助.
- quantity[x] 表示x的子节点的个数(包含自身). 对于根节点来说, 就是这个集合的大小.
- build(n) 由于规模 n 在变化, 所以需要多大的规模就只初始化那一部分即可.
- findx(x) 找到x所在集合的根节点的编号, 并且将路径压缩至1(每个节点直接指向父节点)
- mergexy(x,y) 按照rank大小合并, 并且更新quantity和rank
- 下面的代码中, 如果将 while(m-- && scanf("%d",&k)==1) 改为: while(scanf("%d",&k)==1 && m--) 则会出现错误, 因为当m == 0 的时候本来没有group, k应该没有, 但是还是scanf将下一组的数据输入了, 所以会造成TLE.
- 代码如下:
#include <stdio.h> #define MAXNUM 30100 //parent[i]记录i号节点对应的父节点编号. 它们属于同一个集合. int parent[MAXNUM]; //quantity[i]记录指向i或者i的子节点的节点个数.对于根节点即集合大小(指向自身). int quantity[MAXNUM]; //rank[i] 记录i的深度, 以便建立一棵平衡的树. int rank[MAXNUM]; void build(int n){ for(int i=0;i<n;i++){ parent[i] = i; rank[i] = 0; quantity[i] = 1; } } // 找到x所在的集合的根节点.并压缩路径 int findx(int x){ int r = x; int t; while(parent[r] != r){ r = parent[r]; } //将r的所有间接子节点直接指向r本身, 压缩了路径. while(r != x && parent[x] != x){ t = parent[x]; parent[x] = r; x = t; } return x; } // 将x,y所在的集合合并 void mergexy(int x, int y){ int rootx = findx(x); int rooty = findx(y); if (rootx == rooty) return; // 根据秩的大小合并 if(rank[rootx] > rank[rooty]){ parent[rooty] = rootx; quantity[rootx] += quantity[rooty]; }else if(rank[rootx] == rank[rooty]){ parent[rootx] = rooty; quantity[rooty] += quantity[rootx]; rank[rooty]++; }else{ parent[rootx] = rooty; quantity[rooty] += quantity[rootx]; } } int main(){ int n,m; while(scanf("%d%d",&n,&m) && (n || m)){ //初始化数据结构 build(n); int k,x,y; while(m-- && scanf("%d",&k)==1){ scanf("%d",&x); for(int i=1;i<k;++i){ scanf("%d",&y); mergexy(x,y); } } printf("%d ",quantity[findx(0)]); } return 0; }