一中校运会之百米跑

题目背景

在一大堆秀恩爱的 ** 之中,来不及秀恩爱的苏大学神踏着坚定(?)的步伐走向了 100100 米跑的起点。这时苏大学神发现,百米赛跑的参赛同学实在是太多了,连体育老师也忙不过来。这时体育老师发现了身为体育委员的苏大学神,便来找他帮忙。

可是苏大学神需要热身,不然跑到一半就会抽(筋)、于是他就找到了你。。。如果你帮助体育老师解决了问题,老师就会给你 55 个积分。

题目描述

假设一共有 NN(2\leq N\leq 2\times 10^42≤N≤2×104)个参赛选手。(尼玛全校学生都没这么多吧)

老师会告诉你这 NN 个选手的名字。

接着会告诉你 MM(1\leq M\leq 10^61≤M≤106)句话,即告诉你学生 A 与学生 B 在同一个组里。

如果学生 A 与学生 B 在同一组里,学生 B 与学生 C 也在同一组里,就说明学生 A 与学生 C 在同一组。

然后老师会问你 KK(1\leq K\leq 10^61≤K≤106)句话,即学生 X 和学生 Y 是否在同一组里。

若是则输出 Yes.,否则输出 No.

输入格式

第一行输入 NN 和 MM。

接下来 NN 行输入每一个同学的名字。

再往下 MM 行每行输入两个名字,且保证这两个名字都在上面的 NN 行中出现过,表示这两个参赛选手在同一个组里。

再来输入 KK。

接下来输入 KK 个体育老师的询问。

输出格式

对于每一个体育老师的询问,输出 Yes. 或 No.

输入输出样例

输入 #1复制

10 6
Jack
Mike
ASDA
Michel
brabrabra
HeHe
HeHE
papapa
HeY
Obama
Jack Obama
HeHe HeHE
brabrabra HeHe
Obama ASDA
papapa Obama
Obama HeHE
3
Mike Obama
HeHE Jack
papapa brabrabra

1.

这道题的第一眼就是:字符串+板子并查集!!!

遵循政治老师的教导,本题解将从是什么,为什么,怎么做三个方面来为大家阐述并查集的相关知识以及本题做法。

好了,闲话不多说,不会这道题的童鞋们跟我来!


STEP 1 什么是并查集

并查集,是用来求两者是否同属一个单位的一种思路。
仅由一个内容仅仅两行左右的函数以及一个数组就可以实现。
优点:方便快捷,使用有效。
缺点:emmm……没有什么大的缺点,非要有的话可能就是时间复杂度有些高,
     但是这个是可以通过一个小优化解决哒!

STEP 2 为什么要用并查集

并查集是用来判断两者是否为一个单位的方法。

如果同属于一个单位的人为一个战队,那么通过标记和函数查找,我们可以清楚地知道这一个战队的队长是谁,并且以这个队长为这个战队的标志,以后战队的每一个人与其他人碰面,只要标志不同,那就把另一个拉近自己的战队。在访问的时候,只要两个人的标志不同,就是不在一个战队,否则他们就是一个战队的好哥们啦。


STEP 3 并查集怎么做

1.并查集标准判断标志是否相同函数

//只是个例子
int f[100];//定义一个数组,当然这个数组初始化的时候每个人的队长都是他自己
int find(int x){
	if (f[x]==x) return x;//如果队长是他自己,那么他就是队长
	return find(x); //否则一直搜,直到搜到队长为止
}

但是,你会发现,如果每一次碰到一个人就一直搜队长,是不是太慢了,于是我们可以优化一下,只要沿路碰到的全将他的队长设为最高队长。

//依然是个例子
int f[100];
int find(int x){
	if (f[x]==x) return f[x];
	return f[x]=find(f[x]);//沿路的都顺便将他标记
}

2.针对这道题的转化函数

怎么在不用map的情况下定位跟踪到某一个名字的位置呢?这时候就需要新建一个函数来定位。

string mz[100];//大小随便开的,用来直接储存输入的名字
int fname(string name){//输入一个字符串也就是名字
	for (int i=1;i<=n;i++){
		if (name==mz[i]) return i;//不断搜索,返回他的位置
	}
}

STEP 4 总AC代码带注释

#include<bits/stdc++.h>
using namespace std;
int n,m,k;//储存的相应东西如题所示
string f[20001],mz[20001];//分别储存每个人的队长标志以及序号对应的人名
int fname(string name){//转换函数
	for (int i=1;i<=n;i++){
		if (name==mz[i]) return i;
	}
}
string find(string name){//并查集函数
	if (f[fname(name)]==name) return name;
	return f[fname(name)]=find(f[fname(name)]);//将名字转换成数字储存
} 
int main(){
    scanf("%d %d",&n,&m);
    for (int i=1;i<=n;i++){
    	cin>>f[i];
    	mz[i]=f[i];
	} //正常输入
	for (int i=1;i<=m;i++){
		string a,b;
		cin>>a>>b;
		f[fname(find(a))]=find(b);//并查集找队长操作
	}
	scanf("%d",&k);
	for (int i=1;i<=k;i++){
		string a,b;
		cin>>a>>b;
		if (find(a)==find(b)) printf("Yes.\n");
		else printf("No.\n");//寻找队长是否相同并输出
	}
    return 0;//好习惯要养成哦!
}

 


但是,还有一个更好用的东西:STL中的map

直接建立一个由字符串到字符串的映射代替数组

然后把并查集的模板粘上去改一改就成了

(没错,就是这么水)


代码来了~

#include<iostream>
#include<cstdio>
#include<map>//map库
using namespace std;
map<string,string> a;//建立映射
string fin(string x){//查找字符串x的祖先
	if(a[x]==x) return x;
	else return a[x]=fin(a[x]);//路径压缩
}
int main(){
	int n,m,k;
	string s1,s2;//s1,s2可重复使用
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		cin>>s1;//选手名字
		a[s1]=s1;//每个人的祖先初始化为自己
	}
	for(int i=1;i<=m;++i){
		cin>>s1>>s2;//两位选手
		string x1=fin(s1),x2=fin(s2);//合并
		if(x1!=x2) a[x1]=x2;
	}
	cin>>k;
	for(int i=1;i<=k;++i){
		cin>>s1>>s2;
		string x1=fin(s1),x2=fin(s2);//查询
		if(x1!=x2) printf("No.\n");
		else printf("Yes.\n");
	}
	return 0;//后话:STL大法好!
}

 


你AC了吗?AC了就点个赞呗

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值