7-5 看照片找基友 (20 分)

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;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值