Friendship
一个典型的并查集问题,由朋友的朋友也是我的朋友这个关系可以知道。
其实简单可以理解成一个集合。只要是朋友,就纳入这个朋友圈。(集合)但是其实要在集合理搜索是很慢的,所以需要一个新的结构来加快速度。
此题特殊的点在于,所有的点初始朋友数为1,因为自己是自己的朋友。
转化为求树的高度的问题。
并查集转成一颗树来理解。树中某个点的朋友个数,可以求该点所在的树的叶子结点个数。
简单来说就是用基本的 WeightedUnion(int x, int y)来计算。
可能会分成以下几个集合:
查找操作:
利用坍塌规则:
这样的查找,会把parent数组改变。
合并操作
//合并操作(常规)
void Union(int i, int j)
{
parent[i] = j;
}
//合并操作的升级
void WeightedUnion(int x, int y)
{
int fx, fy;
fx = find(x);
fy = find(y);
if (fx == fy)
return;
if (countn[fx] < countn[fy])
{
r = fx;
fx = fy;//把小的合并到大的上面;
fy = r;
}
parent[fy] = fx;
//countn[fx] = countn[fx] + countn[fy];
}
答案:
#include<string.h>
#include<iostream>
using namespace std;
#define Maxsize 200000
int parent[Maxsize];
int countn[Maxsize]; //计数
int n,r;
//查找操作
int find(int x) {
while (x != parent[x])
x = parent[x];
return x;
}
/*
int collapsingFind(int i)
{
int r = i;
for (r = i; parent[r] >= 0; r = parent[r]); //第一步找到该点的根
while (i != r) //把一整个分支的结点都用一个根来代替
{
int s = parent[i];
parent[i] = r;
i = s;
}
return r;
}
*/
//合并操作(常规)
void Union(int i, int j)
{
parent[i] = j;
}
//合并操作的升级
void WeightedUnion(int x, int y)
{
int fx, fy;
fx = find(x);
fy = find(y);
if (fx == fy)
return;
if (countn[fx] < countn[fy])
{
r = fx;
fx = fy;//把小的合并到大的上面;
fy = r;
}
parent[fy] = fx;
countn[fx] = countn[fx] + countn[fy];
}
int main()
{
int N, M, n, m, s = 0; char ch;
int fl = 0;
int num = 1;
int a= 0;
while (cin >> N >> M)
{
for (int j = 1; j <= N; j++)
{
parent[j] = j;
countn[j] = 1;
}
int flag = 1;
if (fl++)
cout << endl;
if (flag)
{
cout << "Case " << num << ":" << endl;
flag = 0;
num++;
}
for (int i = 0; i < M; i++)
{
cin >> ch;
if (ch == 'M')
{
cin >> n >> m;
WeightedUnion(n, m);
}
else
{
cin >> s;
a= find(s);
cout << countn[a]<<endl;
//cout << -1*parent[a]<< endl;
}
}
}
return 0;
}