解题报告-PAT-File Transfer

解题报告-PAT-File Transfer

原题链接:https://pta.patest.cn/pta/test/1342/exam/4/question/21732

题意描述:

输入N个节点和一系列字符串序列:

5
C 3 2
I 3 2
C 1 5
I 4 5
I 2 4
C 3 5
S
其中C表示,判断当前3 2节点是否连通,如果连通输出yes,不连通输出no。

I表示,将节点2 4并到一个集合中。

S表示输入结束。

使用优化的并查集解题:

# include <stdio.h>
# include <cstdio>
# include <iostream>
# define MAX 10100
using namespace std;
int S[MAX];

void Initial(int n){
	for(int i=0;i<=n;i++)
		S[i] = -1;
} 

/*
递归形式可以这么写
int Find(int x){  
    if(S[x] != x)  
        S[x]=Find(S[x]);
    return S[x];  
} 
*/

int Find(int x){
	int k=x,t; //k表示没合并前的节点。
	//在并查集S中查找并返回包含x的树根
	while(S[x] >= 0) //根节点的值都是小于零的 
		x=S[x]; 
	//x节点是根节点 
	//从k节点开始,k节点到根节点所经过的节点,全都连接到根节点上
	while(k != x) { 
		t=k; 
		k = S[k]; 
		S[t] = x;
	}
	return x; 
} 

int Union(int x,int y){ //启发式合并 
	int fx,fy;
    fx = Find(x);
    fy = Find(y);
	if(fx!=fy){
		// 并到多的一方 
		if(-(S[fx]) > -(S[fy])){
			S[fx] = S[fx] + S[fy];
			S[fy] = fx;
		}else{
			S[fy] = S[fx] + S[fy];
			S[fx] = fy;
		} 
		return 1;
	}else return 0;//代表 x,y同属于一个集合 	
}
int main(){
	int N,c1,c2;
	char m;
	cin >> N;
	Initial(N); 
	cin >> m;
	while(m!='S'){
		scanf("%d %d",&c1,&c2);
		if(m=='I'){
			Union(c1,c2);
		}else{
    		if(Find(c1) == Find(c2))
    			printf("yes\n");
    		else
    			printf("no\n");
		}
		cin >> m;
	}
	int co = 0;
	for(int j=1;j<=N;j++){
		if(S[j] < 0)
			co++;
	}
	if(co == 1)
		printf("The network is connected.\n");
	else 
		printf("There are %d components.\n",co);
	return 0; 
}
注意几个地方:

1)并查集的查找可以改为递归方式,更简洁,但不好理解。

2)输入有可能没有操作,直接输入N之后便输入S结束,这时候输出有N个组成部分。

3)这道题的并查集数组就能搞定,如果需要存储额外的信息,可以参考严蔚敏数据结构的介绍。

# define SIZE 100
//存储结构: 双亲表示法
struct TreeNode{ //节点结构
	char nameId; //节点编号A,B,C...也可以是其他类型,也可以加成员
	int parent;  //双亲位置域,这个是必须的
}TreeNode;
TreeNode treeNode[SIZE+1];

//查操作  路径压缩
int find(TreeNode &treeNode,int x){//确定x所在的子集
	if(i<1 || i>100)//>100在实际问题中可以是>n,n为节点总数
		return -1;//合法性验证
	for(int i=x;treeNode[i].parent>0;i=treeNode[i].parent)
				//如果i号节点的双亲节点大于0,表示还没找到根节点,继续找
	//路径压缩
	for(int k=x;k!=i;k=treeNode[k].parent)//从x节点开始,x节点到根节点经过的所有节点都连接到根节点上。
		treeNode[k].parent=i;  //经过点的双亲是i,i是上一个循环中找到的集合的根节点

	return i;//返回集合根节点
}

//并操作 启发式搜索
void meget(TreeNode &treeNode,int x,int y){
	//传过来x,y需要先查询所在的子集
	int fx=find(x);
	int fy=find(y);
	//查询操作也可以放在外面进行,这样就不需要查询,直接合并。
	if(treeNode[fx].parent>treeNode[fy].parent) //如果fx集合的元素较少,因为parent节点保存的是集合元素的负值。
	{
		treeNode[fy].parent += treeNode[fx].parent; //往多的一方合并
		treeNode[fx].parent=fy;
	}else{
		treeNode[fx].parent += treeNode[fx].parent; //往多的一方合并
		treeNode[fy].parent=fx;
	}
}



  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

随风浪仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值