Time Limit: 1000MS | Memory Limit: 20000K | |
Total Submissions: 18782 | Accepted: 9084 |
Description
Input
Output
Sample Input
100 4 2 1 2 5 10 13 11 12 14 2 0 1 2 99 2 200 2 1 5 5 1 2 3 4 5 1 0 0 0
Sample Output
4 1 1
#include <iostream> #include<stdio.h> #include<stdlib.h> using namespace std; const int MAXN = 30500; /*必须开大点/ int pa[MAXN]; /*p[x】表示x的父节点 int rank[MAXN]; /*rank是x的高度的一个上界/ int num[MAXN];/*num[]//存的集合中的元素个数 void make_init(int x) { pa[x] = x; rank[x] = 0; num[x] = 1; } int find_set(int x) { int r = x, temp; while(pa[r] != r) r = pa[r]; while(x != r) { temp = pa[x]; pa[x] = r; x = temp; } return x; //if(x != pa[x]) //递归压缩。不太好,容易溢出栈
// pa[x] = find_set(pa[x]); //return pa[x]; } void union_set(int x, int y) { x = find_set(x); y = find_set(y); if(x == y)return ; if(rank[x] > rank[y])/*让rank比较高的作为父节点 { pa[y] = x; num[x] += num[y]; } else { pa[x] = y; if(rank[x] == rank[y]) rank[y]++; num[y] += num[x]; } } //answer to 1611 int main() { int n, m, x, y, i, t, j; while(scanf("%d%d", &n, &m)) { if(m==n && n == 0) break; if(m == 0) { printf("1\n"); continue; } for(i = 0; i < n; i++){ make_init(i); } for(i = 0; i < m; i++) { scanf("%d", &t); scanf("%d", &x); for(j = 1; j < t; j++){ scanf("%d", &y); union_set(x, y); x = y; } } x = find_set(0); printf("%d\n",num[x]); } return 0; }
这是朴素查找的代码,适合数据量不大的情况:
int findx(int x)
{
int r=x;
while(parent[r] !=r)
r=parent[r];
return r;
}
下面是采用路径压缩的方法查找元素:
int find(int x) //查找x元素所在的集合,回溯时压缩路径
{
if (x != parent[x])
{
parent[x] = find(parent[x]); //回溯时的压缩路径
} //从x结点搜索到祖先结点所经过的结点都指向该祖先结点
return parent[x];
}
上面是一采用递归的方式压缩路径, 但是,递归压缩路径可能会造成溢出栈,我曾经因为这个RE了n次,下面我们说一下非递归方式进行的路径压缩:
int find(int x)
{
int k, j, r;
r = x;
while(r != parent[r]) //查找跟节点
r = parent[r]; //找到跟节点,用r记录下
k = x;
while(k != r) //非递归路径压缩操作
{
j = parent[k]; //用j暂存parent[k]的父节点
parent[k] = r; //parent[x]指向跟节点
k = j; //k移到父节点
}
return r; //返回根节点的值
}