PTA File Transfer 思路分析及代码解析v1.0
一、前导
1. 需要掌握的知识
- 并查集的基本概念
2. 题目信息
题目来源:PTA / 拼题A
题目地址:https://pintia.cn/problem-sets/16/problems/670
二、解题思路分析
1. 题意理解
已知有N部计算机,默认无网络连接。C n1 n2 表示检查两部计算机之间是否存在网络连接;I n1 n2 表示在两部计算机之间添加网络连接;举例:若n1 n2存在连接、n2 n4存在连接,则n1 n4之间可以传送文件,以此类推
1. 1 输入数据
5 \\'5' 代表网络中共有5台计算机,编号从1到5
C 3 2 \\检查 3#计算机 和 2#计算机 之间是否能够传送文件
I 3 2 \\在3#计算机 和 2#计算机 之间添加一根网线
C 1 5 \\检查 1# 和 5 #是否能够传送文件
I 4 5 \\ 在4#计算机 和 5#计算机 之间添加一根网线
I 2 4 \\ 在2#计算机 和 4#计算机 之间添加一根网线
C 3 5 \\检查 3# 和 5 #是否能够传送文件
S \\结束,统计网络连接情况
1.2 输出数据
no \\ 对应C 3 2,默认无网络连接,因此不能传送文件
no \\对应C 1 5,1# 和 5 #无网络连接,因此不能传送文件
yes \\对应C 3 5,因为此时 3# 2# 4# 5# 之间存在网络连接,所以可以传送文件
There are 2 components. \\ 1# 和 3# 2# 4# 5# ,共2个。若只有一个网络,输出 The network is connected.
1.3 补充
n1 若和其他计算机无网络连接,依据题意 n1也是一个单一的网络(孤岛),在最后的统计中需要单独计算
2. 思路分析(重点)
- 该题属于并查集的应用。并查集是树的一种形态:一父多子多孙。结合File Transfer这道题:(1)C n1 n2 对应并查集中的查(两点间网络是否连通):,只要n1 n2有相同的根节点,n1 n2必连通。(2)I n1 n2对应并查集中的并,将节点n1 n2并到相同的根节点下。代码只要正确实现 并 查 两个操作,就可以突破本题。
三、具体实现
1. 弯路和bug
- 通过Find()查询根节点时,使用while语句会更清晰容易
- 合并时,小树向大树合并,否则查找效率会越来越低(树偏向一边了)
2. 代码框架(重点)
2.1 采用的数据结构
本题通过一维数组来存储数据最方便:数组的下标对应计算机的编号,数组的值就是其其父节点所在位置。对于根节点,其数组值为 负数 ,数组值的绝对值就是树的元素值。所有节点初始都是根节点,数组值都是-1
void Init(int a[],int n)
{
for(int i=0;i<n;i++)
a[i]=root; //every Node is island , root=-1
}
2.2 程序主体框架
首先完成数据的初始化,然后通过switch语句根据关键字 C, I , S分别进行处理:C 对应并查集中的 查询,I 对应并查集中的 合并,S对应统计
程序伪码描述
int main()
{
1.初始化数据
2.Switch语句
{
C: find();
I: merge();
S: count();
}
return 0;
}
2.3 各分支函数
1.void Init(int a[],int n); 初始化函数,数组的下标对应计算机的编号,数组的值就是其其父节点所在位置,默认值均为-1:每个节点都是孤岛
void Init(int a[],int n)
{
for(int i=0;i<n;i++)
a[i]=root; //every Node is island , root=-1
}
- int Find(int a[],int x); 通过find()找到根节点,存储编号 与 实际编号差1,因此执行减1操作。初始化时如果从1开始,则没有该问题
int Find(int a[],int x)
{
x--;
while(a[x]>=0)
x=a[x];
return x;
}
- void Check(int a[],int x,int y); 检查x,y 的根节点是否相同,若相同,则存在网络连接,调用find()找到根节点,然后比对
void Check(int a[],int x,int y)
{
int root1,root2;
root1=Find(a,x);
root2=Find(a,y);
if(root1==root2) cout<<"yes"<<endl;
else cout<<"no"<<endl;
return;
}
- void Merge(int a[],int x,int y); 对应I指令,本质就是并查集中的合并。在合并时,小树向大树合并、合并后统计树的元素个数并更新。小树向大树合并是为了尽量控制树的深度,也就是保持查找效率
void Merge(int a[],int x,int y)
{
int root1,root2;
root1=Find(a,x);
root2=Find(a,y);
if(root1!=root2 && a[root1]<=a[root2] )
{
a[root2]=root1;
a[root1]--;
}
else if(root1!=root2 && a[root1]>a[root2])
{
a[root1]=root2;
a[root2]--;
}
return;
}
- void Count(int a[],int n); 统计结果,值为负数的节点都需要录入
void Count(int a[],int n)
{

最低0.47元/天 解锁文章
1334

被折叠的 条评论
为什么被折叠?



