题2.pta数据结构题集-File Transfer (25分)
一、题目
二、题解
1.简析
并查集的常规题。由于这里电脑的编号是从1到N,所以不妨将一个数组下标当作电脑编号,对应的数组元素直接就是该编号电脑的父电脑编号,若该编号电脑为根电脑,则用一个负数表示它的父电脑,它的绝对值就是这棵树拥有的节点个数(用于按秩归并)。然后按并查集的find,union写就好。
2.代码
下面是第一次做敲的码,未用路径压缩
#include <stdio.h>
#include <stdlib.h>
#define Maxsize 100000
void finish_connect(int set[])//连接操作
{
int c1,c2;
int root1,root2;
scanf("%d%d",&c1,&c2);
root1=find_root(c1,set);//找寻c1,c2的根电脑
root2=find_root(c2,set);
if(root1==root2)//根电脑相同无需连接
{
return;
}
else
{
union_set(root1,root2,set);//通过连接c1,c2的根电脑连接c1,c2
}
return;
}
int check_connect(int set[])//检查两台电脑
{
int c1,c2;
int root1,root2;
scanf("%d%d",&c1,&c2);
root1=find_root(c1,set);
root2=find_root(c2,set);
if(root1==root2)//根电脑相同说明c1,c2连接
{
return 1;
}
else
{
return 0;
}
}
void Check(int set[],int N)//检查所有电脑
{
int i;
int k=0;//用k记录根电脑的个数
for(i=1;i<=N;i++)
{
if(set[i]<0)//set值为负数时方为根电脑
{
k++;
}
}
if(k==1)//当只有一台根电脑时网络全部连通
{
printf("The network is connected.");
}
else
{
printf("There are %d components.",k);
}
return;
}
int find_root(int c,int set[])
{
int root;
for(;set[c]>=0;c=set[c]);
root=c;
return root;
}
void union_set(int root1,int root2,int set[])//按秩归并(大树接上小树)连接电脑
{
//将set[root]设为单个集合元素个数的相反数,从而储存了树的规模
if(set[root1]<set[root2])//root1为大树(注意:由于是负数比大小,所以越小规模越大)
{
set[root1]+=set[root2];//root1树接上root2树,元素个数增加为两树之和
set[root2]=root1;//将root2树奉root1树为根
}
else
{
set[root2]+=set[root1];
set[root1]=root2;
}
return;
}
int main()
{
int N;
int i;
char key;
int set[Maxsize];//由于电脑是按顺序编号所以直接利用数组下标表示电脑,进而用set[]表示该电脑的上头为哪个电脑
int answer[Maxsize];//储存C的答案
for(i=0;i<Maxsize;i++)
{
answer[i]=-1;
}
scanf("%d",&N);
for(i=1;i<=N;i++)//用-1初始化所有电脑,表示都不连通
{
set[i]=-1;
}
i=0;
do
{
key=getchar();
switch(key)
{
case 'I':finish_connect(set);break;
case 'C':answer[i]=check_connect(set);i++;break;
}
}while(key!='S');
for(i=0;answer[i]!=-1;i++)
{
if(answer[i]==1)
{
printf("yes\n");
}
else
{
printf("no\n");
}
}
Check(set,N);
return 0;
}
第二次做敲的码,这个用路径压缩了
#include <bits/stdc++.h>
using namespace std;
int parent[10001];
void init()
{
fill(parent,parent+10001,-1);
return;
}
int find_root(int v)
{
if(parent[v]<0)
{
return v;
}
else
{
return parent[v]=find_root(parent[v]);//路径压缩,会将路径上的节点的父节点都变成根节点,相当于直接将这些节点贴在了根节点上,大大缩短了下一次find的路径
}
}
int check_set(int v1,int v2)
{
int root1,root2;
root1=find_root(v1);
root2=find_root(v2);
if(root1!=root2)
{
return 0;
}
else
{
return 1;
}
}
void union_set(int v1,int v2)
{
int root1,root2;
root1=find_root(v1);
root2=find_root(v2);
if(root1==root2)
{
return;
}
if(parent[root1]<parent[root2])
{
parent[root1]+=parent[root2];
parent[root2]=root1;
}
else
{
parent[root2]+=parent[root1];
parent[root1]=root2;
}
return;
}
int count_set(int N)
{
int num=0;
for(int i=1;i<=N;i++)
{
if(parent[i]<0)
{
num++;
}
}
return num;
}
int main()
{
init();
int N;
cin>>N;
char op;
cin>>op;
while(op!='S')
{
int v1,v2;
cin>>v1>>v2;
switch(op)
{
case 'C':
if(check_set(v1,v2))
{
printf("yes\n");
}
else
{
printf("no\n");
}
break;
case 'I':union_set(v1,v2);break;
}
cin>>op;
}
int num=count_set(N);
if(num>1)
{
printf("There are %d components.\n",num);
}
else
{
printf("The network is connected.\n");
}
}