1.题目
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 betweenc1
andc2
; orC c1 c2
where
C
stands for checking if it is possible to transfer files betweenc1
andc2
; orS
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 betweenc1
andc2
, 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 arek
components." wherek
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.
2.代码
#include<stdio.h>
#include<stdlib.h>
#define Maxsize 100000
int s[Maxsize];
//s[x]:下标x为值 s[x]表示根节点
int Findx(int s[],int x)
{
if(s[x]<0)
return x;//找到根了
else
return s[x]=Findx(s,s[x]);//找父节点的根节点 也是根节点
}
//按规模
void unionx(int s[],int root1,int root2)
{
if(s[root1]<s[root2])//root1规模更大 (负数)
{
s[root1]+=s[root2];
s[root2]=root1;
}
else{
s[root2]+=s[root1];
s[root1]=root2;
}
}
void Initialization(int n)
{
int i;
for(i=0;i<n;i++)
s[i]=-1;
}
void InputConnection()
{
int x1,x2;
scanf("%d %d",&x1,&x2);
int root1,root2;
root1=Findx(s,x1-1);
root2=Findx(s,x2-1);
if(root1!=root2)
unionx(s,root1,root2);
}
void CheckConnection()
{
int x1,x2;
scanf("%d %d",&x1,&x2);
int root1,root2;
root1=Findx(s,x1-1);
root2=Findx(s,x2-1);
if(root1==root2)
printf("yes\n");
else
printf("no\n");
}
void CheckNetwork(int n)
{
int i,ans=0;
for(i=0;i<n;i++)
if(s[i]<0)
ans++;
if(ans==1)
printf("The network is connected.\n");
else
printf("There are %d components.\n",ans);
}
int main()
{
int n;
char in;
scanf("%d",&n);
Initialization(n);
do{
scanf("%c",&in);
switch(in)
{
case 'I':InputConnection();break;
case 'C':CheckConnection();break;
case 'S':CheckNetwork(n);break;
}
}
while(in!='S');
return 0;
}
3.分析
首先是存储,为了节省空间,没有采用结构体存储,基于以下定理:
任何有限集合的N个元素都可以被一一映射为整数0~N-1。
换而言之,给定n个computer,我们就可以用数字下标(0-n-1)表示它,那么数组里面就可以存放他的父节点。
如此一来:s[i]:i为computer,s[i]为其父节点。有一点需要注意,就是数组下标是从零开始的,而computer的编号从1开始,因此编号对应减1.
其次是连接操作,也就是查找是不是同一个根节点。使用Find操作。这里,Findx使用了路径压缩的思想,通过尾递归,将每个节点都直接指向了他的根节点。
如果不是同一个根节点,就使用union操作,将他们合并。这里,union采用了按照规模进行合并,规模小的合并到规模大的。
最后就是检查是否用在同一个根节点,用Find即可。
结束的时候,判断是否连通,也就是看看有几个根节点,遍历检查数组中有几个负值即可。