PTA File Transfer --- 集合操作

这是一个类似于对集合的操作的题

思路

  1. 集合的逻辑结构可以看成是一个树(逻辑结构如下图),每个集合的代表元素为这个树的根,当合并两个元素对应的集合的时候,我们需要知道这写元素对应属于哪个集合,即找到这些集合对应的代表元素(即树根),再进行合并之类的操作
  2. 这个树是子结点指向父结点(集合中的元素保存的是集合代表元素的下标)
    . PTA File Transfer --- 集合操作_第1张图片
  3. 我们把集合建立成一个数组,因为题目中的各个结点都是连续的数,所以可以通过数组的下标来映射到各个结点的数,所以这个数组中保存的值为这个结点对应集合的代表元素的下标,而某个结点为代表元素时,保存的是这个集合全部元素的个数的负数(负数是为了辨认其为集合的代表元素,而保存集合元素个数是为了进行按秩归并)
  4. 我们把题目中的连接操作看成是连接两个集合的概念,每次要连接两个元素对应的集合时,我们先Find这两个元素对应集合的代表元素,比较代表元素的规模(每个集合所含元素的个数),把规模小的加到规模大的结合上(把规模小的对应的树添加到规模大的树上
  5. Find函数实现了路径压缩,要查找某个元素对应集合的代表元素时,从这个结点不断向树根递归,找到树根后再一步步把树根的下标返回(让递归中的元素直接指向树根,使得检下次查这写元素时更方便)
#include 
#include 

int Find(int S[], int X) {
	if (S[X] < 0) {
		return X;
	}
	else
		return S[X] = Find(S, S[X]); //路径压缩
}

//按秩归并
void Union(int S[], int root1, int root2) { //比规模进行
	if (S[root2] < S[root1]) {
		S[root2] += S[root1];
		S[root1] = root2;
	}
	else {
		S[root1] += S[root2];
		S[root2] = root1;
	}
}

void Input_connection(int S[]) {
	int u, v;
	int root1, root2;
	scanf("%d %d\n", &u, &v);
	root1 = Find(S, u - 1);
	root2 = Find(S, v - 1);  //v这个元素在数组中下标为v-1的地方
	if (root1 != root2)
		Union(S, root1, root2);
}

void Check_connection(int S[]) {
	int u, v;
	int root1, root2;
	scanf("%d %d\n", &u, &v);
	root1 = Find(S, u - 1);
	root2 = Find(S, v - 1);  //v这个元素在数组中下标为v-1的地方
	if (root1 == root2)
		printf("yes\n");
	else
		printf("no\n");
}

void Check_network(int S[], int n) {
	int i, counter = 0;
	for (i = 0; i < n; i++) {
		if (S[i] < 0)
			counter++;
	}
	if (counter == 1)
		printf("The network is connected.\n");
	else
		printf("There are %d components.\n", counter);
}

void Init(int S[], int n) {
	for (int i = 0; i < n; i++) {
		S[i] = -1;
	}
}

int main() {
	int S[10000]; //对于结点-1的为在数组中下标,而其中的值为父结点的下标,父结点的值为集合元素的个数的负值
	int n;
	char in;
	scanf("%d\n", &n);
	Init(S, n);
	do {
		scanf("%c", &in);
		switch (in) {
			case'I':Input_connection(S); break;
			case'C':Check_connection(S); break;
			case'S':Check_network(S, n); break;
		}
	} while (in != 'S');
	return 0;
}

你可能感兴趣的:(#,PTA)