11、并查集:检查网络
【问题描述】
给定一个计算机网络以及机器间的双向连线列表,每一条连线允许两端的计算机进行直接的文件传输,其他计算机间若存在一条连通路径,也可以进行间接的文件传输。请写出程序判断:任意指定两台计算机,它们之间是否可以进行文件传输?
【输入要求】
输入若干测试数据组成。对于每一组测试,第1行包含一个整数N(≤10000),即网络中计算机的总台数,因而每台计算机可用1到N之间的一个正整数表示。接下来的几行输入格式为I C1 C2或者 C或者C C1C2或者S,其中C1和C2是两台计算机的序号,I表示在C1和C2间输入一条连线,C表示检查C1和C2间是否可以传输文件,S表示该组测试结束。当N为0时,表示全部测试结束,不要对该数据做任何处理。
【输出要求】
对每一组C开头的测试,检查C1和C2间是否可以传输文件,若可以,则在一行中输出“yes”,否则输出“no”。当读到S时,检查整个网络。若网络中任意两机器间都可以传输文件,则在一行中输出“The network is connected.”,否则输出“There are kcomponents.”,其中k是网络中连通集的个数。两组测试数据之间请输出一空行分隔。
最简单的数据结构作业了,就是一道acm题,也不用什么乱七八糟的话,仅仅百行以内的代码就搞定。
跟题目一个样子就是一道并查集的问题:
把两种典型的并查集问题联系到了一起:1、查询两个是否是连通的。2、看总共有几个连通区,跟畅通工程是一样的问题。
再注意一些细节的输入输出的处理即可:
自己还编了一组测试数据,进行测试:
还有用了路径压缩的优化方法:
#include <iostream>
#include <stdio.h>
using namespace std;
#define maxn 10002///N(≤10000)
int set[maxn];///设置操作数组
int rank[maxn];///按秩压缩的秩数
void init(int n)
{
for(int i=1;i<=n;i++)
{
set[i]=i;///初始化,把父节点都设为自己本身
rank[i]=1;///设秩的初值
}
}
int find(int x)///带路径压缩的向上查找
{
if(set[x]==x)
return x;
return set[x]=find(set[x]);
}
void union_set(int a,int b)///合并,当执行I操作即把两台电脑连接在一起的时候,将父节点设在一起即可
{
int ta=find(a);
int tb=find(b);
if(ta==tb)
return ;
if(ta!=tb)
set[ta]=set[tb];
}
int main()
{
int n;
char c;
int a,b;
while(~scanf("%d",&n),n){///当读到0的时候,所有操作结束
init(n);///对一组操作数,先初始化
while(1)
{
scanf("%c",&c);///输入操作符
if(c=='C'){///C操作表示查询两台电脑是否相连
scanf(" C%d C%d",&a,&b);///读数据的操作处理不是特别好
if(find(a)==find(b))///相连“yes”
printf("yes\n");
else
printf("no\n");
}
if(c=='I')///I操作表示将两台电脑,连接在一起
{
scanf(" C%d C%d",&a,&b);
union_set(a,b);///使用union连接操作
}
if(c=='S')///S操作,查询整个网络连通情况。并且表示该组数据全部结束
{
int tmp=0;///用来标记有几个连通区
for(int i=1;i<=n;i++)
{
if(set[i]==i)
tmp++;
}
if(tmp==1)///只有一个连通区的时候,表示全部连接
printf("The network is connected.\n");
else///否则要输出有多少个并且输出个数
printf("There are %d components.\n",tmp);
break;
}
}
}
return 0;
}
/**测试数据
输入:
5
I C1 C2
I C2 C3
C C1 C3
C C2 C4
S
10
I C3 C4
I C4 C5
I C6 C7
I C8 C9
I C7 C9
C C6 C8
C C3 C5
C C1 C10
S
3
I C1 C2
I C2 C3
S
0
输出
yes
no
There are 3 components.
yes
yes
no
There are 5 components.
The network is connected.
**/