05-树8 File Transfer (25分)

题目描述

We have a network of computers and a list of bi-directional connections. Each of these connections allows a file transfer from one computer to another. Is it possible to send a file from any computer on the network to any other?

Input Specification:

Each input file contains one test case. For each test case, the first line contains N (2≤N≤10000), the total number of computers in a network. Each computer in the network is then represented by a positive integer between 1 and N. Then in the following lines, the input is given in the format:

I c1 c2  

where I stands for inputting a connection between c1 and c2; or

C c1 c2

where C stands for checking if it is possible to transfer files between c1 and c2; or

S

where S stands for stopping this case.

Output Specification:

For each C case, print in one line the word "yes" or "no" if it is possible or impossible to transfer files between c1 and c2, respectively. At the end of each case, print in one line "The network is connected." if there is a path between any pair of computers; or "There are k components." where k is the number of connected components in this network.

Sample Input 1:

5
C 3 2
I 3 2
C 1 5
I 4 5
I 2 4
C 3 5
S

Sample Output 1:

no
no
yes
There are 2 components.

Sample Input 2:

5
C 3 2
I 3 2
C 1 5
I 4 5
I 2 4
C 3 5
I 1 3
C 1 5
S

Sample Output 2:

no
no
yes
yes
The network is connected.

解题思路

这道题是典型的并查集问题,我们可以用树来表示每一个集合,用树的根结点值来代表集合名,又根据这道题的输入数据,我们可以用一个数组实现这些树,数组的下标对应电脑标号减1,元素值为其父结点的下标,根结点的值为-结点数。由题可知,需要处理ICS三种情况。对于I来说,我们首先需要得到两个数据对应的集合,若不同则合并,若相同则不管。于是这就可以抽象成两个问题,即得到数据对应的集合与合并两个集合。对于C来说,我们只需得到两个数据对应的集合,若相同打印yes,否则打印no。对于S来说,我们只需遍历数组,统计值小于0的元素个数,然后根据情况打印不同的字符串。

代码

#include <stdio.h>

#define MAXSIZE 10000

typedef int SetName;            //用根结点的值表示集合
typedef int SetType[MAXSIZE];   //用一个数组表示集合,下标是电脑标号减一,值是父结点的下标

void init(SetType set, int N);
SetName getSetName(SetType set, int v);
void merge(SetType set, SetName root1, SetName root2);
void checkConn(SetType set);
void connection(SetType set);
void checkSet(SetType set, int N);

int main() {
    int N;
    char op;
    SetType set;
    scanf("%d\n", &N);
    init(set, N);
    do {
        scanf("%c", &op);
        switch (op) {
            case 'C':
                checkConn(set); break;
            case 'I':
                connection(set); break;
            case 'S':
                checkSet(set, N); break;
        }
    } while (op != 'S');
    return 0;
}

//将数组前N个数初始化为-1
void init(SetType set, int N) {
    for (int i = 0; i < N; i++) {
        set[i] = -1;
    }
}

//得到下标v对应的集合名
SetName getSetName(SetType set, int v) {
    for ( ; set[v] >= 0; v = set[v]);
    return v;
}

//求两个集合的并集,为了尽可能地降低集合高度,将结点数小的集合的根连到结点数大的集合的根
void merge(SetType set, SetName root1, SetName root2) {
    if (set[root1] < set[root2]) {  //集合1结点更多
        set[root1] += set[root2];
        set[root2] = root1;
    } else {
        set[root2] += set[root1];
        set[root1] = root2;
    }
}

//判断两个值是否属于同一集合
void checkConn(SetType set) {
    int a, b;
    scanf("%d %d\n", &a, &b);
    SetName root1, root2;
    root1 = getSetName(set, a - 1);
    root2 = getSetName(set, b - 1);
    if (root1 == root2) printf("yes\n");
    else printf("no\n");
}

//将两个值对应的集合并起来
void connection(SetType set) {
    int a, b;
    scanf("%d %d\n", &a, &b);
    SetName root1, root2;
    root1 = getSetName(set, a - 1);
    root2 = getSetName(set, b - 1);
    if (root1 != root2) merge(set, root1, root2);
}

//统计根结点个数,根据情况输出
void checkSet(SetType set, int N) {
    int count = 0;
    for (int i = 0; i < N; i++) {
        if (set[i] < 0) count++;
    }
    if (count == 1) printf("The network is connected.\n");
    else printf("There are %d components.\n", count);
}
posted @ 2020-03-24 22:29  AndyHY  阅读(223)  评论(0编辑  收藏  举报