题目背景
在一大堆秀恩爱的 ** 之中,来不及秀恩爱的苏大学神踏着坚定(?)的步伐走向了 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了就点个赞呗