#include<iostream>
#include<fstream>
using namespace std;
//#define DEBUG
static int p[30000];
static int num[30000];
int findset(int x)
{
if (x != p[x])
p[x] = findset(p[x]);
return p[x];
}
void unionset(int s1, int s2)
{
int x, y;
x = findset(s1);
y = findset(s2);
if (x == y)
return;
if (num[x] > num[y])
{
p[y] = x; num[x] += num[y]; num[y] = 0;
}
else
{
p[x] = y; num[y] += num[x]; num[x] = 0;
}
}
int main()
{
#ifdef DEBUG
fstream cin("G:\\book\\algorithms\\acm\\Debug\\dat.txt");
#endif
int n, m;
while (cin >> n >> m)
{
if (n == 0 && m == 0)
break;
int i, j;
for (i = 0; i < n; i++)
{
p[i] = i; num[i] = 1;
}
for (j = 0; j < m; j++)
{
int k;
cin >> k;
int a, b;
cin >> a;
for (i = 1; i < k; i++)
{
cin >> b;
unionset(a, b);
}
}
cout << num[findset(0)] << "\n";
}
return 0;
}
相关数据结构的基本知识参见 算法导论 21.3 不相交集合森林 的相关内容。以数组取代链式结构做了一定简化。
两种启发策略: 按秩合并, 路径压缩
其中按秩合并在其中没有应用,取代的是对每个集合树的元素的数目进行纪录,按照集合的大小进行合并。
路径压缩和教材上一致。
另外在编程的过程中uniouset(s1,s2)对s1, s2可能已经属于同一集合的情况考虑不周导致出现错误。