【洛谷】家谱(并查集,map)

本文介绍了一种通过编程解决家族血统问题的方法,利用父子关系数据,采用两种不同的算法来找出一个人的最早祖先,包括使用map进行路径压缩优化和通过编号处理提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目背景

现代的人对于本家族血统越来越感兴趣。

题目描述

给出充足的父子关系,请你编写程序找到某个人的最早的祖先。

输入格式

输入由多行组成,首先是一系列有关父子关系的描述,其中每一组父子关系中父亲只有一行,儿子可能有若干行,用 #name 的形式描写一组父子关系中的父亲的名字,用 +name 的形式描写一组父子关系中的儿子的名字;接下来用 ?name 的形式表示要求该人的最早的祖先;最后用单独的一个 $ 表示文件结束。

输出格式

按照输入文件的要求顺序,求出每一个要找祖先的人的祖先,格式为:本人的名字 + 一个空格 + 祖先的名字 + 回车。

输入输出样例

输入 #1

#George
+Rodney
#Arthur
+Gareth
+Walter
#Gareth
+Edward
?Edward
?Walter
?Rodney
?Arthur
$

输出 #1

Edward Arthur
Walter Arthur
Rodney George
Arthur Arthur

说明/提示

规定每个人的名字都有且只有 6 个字符,而且首字母大写,且没有任意两个人的名字相同。最多可能有 10310组父子关系,总人数最多可能达到 5×10 人,家谱中的记载不超过 30 代。

方法一:巧用map处理,直接用map< string string >储存关系(first当下标,second当值),注意这里的初始化

#include<bits/stdc++.h>
using namespace std;
map<string,string> parent;
//找祖宗,路径压缩
string find(string x){
	if(x==parent[x])return x;
	return parent[x]=find(parent[x]);
}
string father;//father一定开在循环外面,否则father值会被重新定义
int main(){
	string name;
	while(cin>>name&&name[0]!='$'){
		string newname;
		for(int i=1;i<name.length();i++){//删掉第一个表示符
			newname+=name[i];
		}
		string son;
		if(name[0]=='#'){//是爸爸
			father=newname;
			if(parent[father]=="")parent[father]=father;//一定要先初始化爸爸的祖宗是自己,要不然就没法判断祖宗是谁了
		
		}else if(name[0]=='+'){//是儿子
			parent[newname]=father;//认爸爸
		}else{
			cout<<newname<<' '<<find(newname)<<endl;
		}
	}
} 

方法二:给每个人编号,对编号处理,注意不能按秩合并,要不然会改变祖宗

#include<bits/stdc++.h>
using namespace std;
map<string,int> mp1;//名字,身份 
map<int,string> mp2;//身份,名字 
set<string> se;
long long a[100000],rank[100000];
//找祖宗,路径优化 
int find(int x){
	if(a[x]==x)return x;
	return a[x]=find(a[x]);
}
int main(){
	string name;
	int id=-1;
	int father;
	for(int i=0;i<1000;i++){//初始化 
		a[i]=i;
	} 
	while(cin>>name&&name[0]!='$'){
		string newname;
		for(int i=1;i<name.length();i++){//去掉第一个标识符 
			newname+=name[i];
		}
		if(se.count(newname)==0){//新人物,给身份 
			id++;
			mp1[newname]=id;
			mp2[id]=newname;
			se.insert(newname);
		}
		if(name[0]=='#'){//是父亲 
			if(se.count(newname)==0){//新人物使用新身份 
				father=id;
			}else{
				father=mp1[newname];//旧人物用以前身份 
			}
			
		}else if(name[0]=='+'){//是儿子 
			if(se.count(newname)==0){//新人物给身份 
				a[find(id)]=find(father);
			}else {
				a[find(mp1[newname])]=find(father);//出现过用以前身份 
			}
		}else{//询问 
			cout<<newname<<" "<<mp2[find(mp1[newname])]<<endl;	
		}
		
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值