7-5 看照片找基友 (20 分)
题目
小A是个单身狗,他有很多好基友,他们平时喜欢出去聚会和旅游,每次聚会都会照一张集体照上传到群共享,有一天,小A整理照片,想通过照片来看看他们这群基友的情况。我们假定,在同一张照片里同时出现的,两两之间都是好基友,基友的基友也是好基友。那么问题来了,你能帮小A确定任意的两个人是否好基友吗?
输入格式
首先输入照片的张数N(N<10000),之后N行,按以下格式输入照片里的情况:
K P1 P2 P3 …… PK
其中K是照片中的人数,后面的K个数是照片中人的编号,我们假定从1开始连续编号,人数不超过10000。 接下来输出查询的个数T(T<10000),之后T行,每行输入两个数,分别是查询的两个人的编号。
输出格式
首先输出基友圈的个数和所有人数,之后对T组查询,在一行中输出查询结果,如果两个是好基友,输出Yes,否则输出No。
输入样例
4
3 11 1 2
3 3 4 10
4 1 5 7 8
3 9 6 12
2
10 5
7 11
输出样例
3 12
No
Yes
基本思路
简单的并查集问题。
我一直被卡在最后一个测试点,原因是我没有注意到题目条件----我们假定从1开始连续编号,人数不超过10000----所以最大的人物编号就是总人数cnt2
我们可以记录合并次数cnt1,而基友圈数cnt1=总人数cnt2-合并次数cnt1。
代码
#include <bits/stdc++.h>
using namespace std;
int n,k,m;//照片数量,一张照片上的人数,测试样例组数
int cnt1=0,cnt2=0;//基友圈的个数,所有人数
vector<int> father;//下标为人物编号,值为该人物的父结点的编号
//查找
int findFather(int v){
if(father[v]==v){
return father[v];
}else{
//路径压缩
int F=findFather(father[v]);
father[v]=F;
return F;
}
}
//合并
void Union(int a,int b){
int faA=findFather(a);
int faB=findFather(b);
if(faA!=faB){
father[faA]=faB;
cnt1++;//合并过几次
}
}
int main(){
cin>>n;
//初始化
father.resize(10005);
for(int i=1;i<=10005;i++) father[i]=i;
//读入所有照片信息
for(int i=1;i<=n;i++){
int ans1,ans2;
cin>>k;
cin>>ans1;
//因为我们假定从1开始连续编号,人数不超过10000,所以最大的人物编号就是总人数
if(ans1>cnt2) cnt2=ans1;
for(int j=0;j<k-1;j++){
cin>>ans2;
//因为我们假定从1开始连续编号,人数不超过10000,所以最大的人物编号就是总人数
if(ans2>cnt2) cnt2=ans2;
Union(ans1,ans2);
}
}
//读入所有测试样例并做出判断
cin>>m;
cnt1=cnt2-cnt1;//基友圈个数=总人数-合并次数
cout<<cnt1<<" "<<cnt2<<endl;
while(m--){
int ans1,ans2;
cin>>ans1>>ans2;
if(findFather(ans1)==findFather(ans2)){
cout<<"Yes"<<endl;
}else{
cout<<"No"<<endl;
}
}
}