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≤104), 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.
个人分析:
题目说的是:现在有一堆电脑,它们互相之间初始没有连接成网,现在根据输入进行连接,且边连接(I c1 c2)边检查(C c1 c2)特定电脑之间有没有接好。如果接好了,就输出yes,反之no。最后,需要判断这堆电脑有几个不相连的子集,如果是一个那就是直接全连通了。
很明显的考察并查集操作的题目,课件教的用树来表示子集。由于每个电脑(结点)信息只用一个数字表示,我们不需要建立特殊结点结构体,直接用数组(结点数值=数组下标=结点地址;数组数值=父结点地址(下标)=父结点数值)即可。
代码如下:
#include<stdio.h>
#define Maxsize 10000
//初始化,各个结点均为头结点,头结点数组值赋为-1(也就是一开始的 负树总结点数)
void Initialize(int T[],int n)
{
int i;
for(i=0;i<n;i++)
{
T[i]=-1;
}
}
//联合,将倆树按秩合并
void Union(int T[],int Root1,int Root2)
{
if(T[Root1]>T[Root2])
{
T[Root2]+=T[Root1];
T[Root1]=Root2; //将小树的头结点指向大树,大树根结点赋值为负二者总结点数
}
else
{
T[Root1]+=T[Root2];
T[Root2]=Root1;
}
}
//查找,找到当前结点的根结点
int Find(int T[],int X)
{
for(;T[X]>=0;X=T[X]);
return X;
}
//连接俩台计算机,先找到俩元素各自所在的集合根结点,再将二者联合
void Input_connection(int T[])
{
int u,v;
int Root1,Root2;
scanf("%d %d",&u,&v);
getchar();
Root1=Find(T,u-1);
Root2=Find(T,v-1);
if(Root1!=Root2)
Union(T,Root1,Root2);
}
//检查俩台计算机是否连接,到俩元素各自所在的集合根结点,对比是否相同
void Check_connection(int T[])
{
int u,v;
int Root1,Root2;
scanf("%d %d",&u,&v);
getchar();
Root1=Find(T,u-1); //数组下标从0开始,输入从1开始,这里转换一下
Root2=Find(T,v-1);
if(Root1==Root2)
printf("yes\n"); //若两个结点所在树的根结点相同,两结点在同一树上(这不废话嘛...)
else
printf("no\n");
}
//检查所有计算机是否完全连接
void Check_Tree(int T[],int n)
{
getchar();
int counter=0;
for(int i=0;i<n;i++)
{
if(T[i]<0)
counter++;
}
if(counter==1)
printf("The network is connected.\n");
else
printf("There are %d components.",counter);
}
int main()
{
int n;char in; //in记录操作:连接or检查
int Tree[Maxsize];
scanf("%d",&n);
getchar();
Initialize(Tree,n);
do{
scanf("%c",&in);
switch(in){
case 'I': Input_connection(Tree);break; //连接操作
case 'C': Check_connection(Tree);break; //检查连接操作
case 'S': Check_Tree(Tree,n);break; //检查子集数操作
}
}while(in!='S'); //输入S时跳出
return 0;
}
总结:小小一个数组,,以前从没想过,它能表示:树,堆,队列,堆栈,并查集合。。。。等等。菜鸡觉得,这和数组元素顺序储存,且每个元素都有一个特定下标表示等特点,有很大关系。
首先,数组元素其实包含了两个值(元素值+数组下标(地址)),它不像链表必须通过遍历才能到达某个地址,因为其地址就是下标。
第二,上述一些数据结构,都可表示成数据密集的情况,用数组很划算(空间利用率高)。
第三。。。。。。。嗯。。。ZZZZZZZZ