|邻接表--关系图dfs|7-12 愿天下有情人都是失散多年的兄妹 (25分)

该博客介绍了如何利用图的邻接链表结构和深度优先搜索算法,解决寻找五代内共同祖先的问题。针对给定的人物关系,通过构建关系图并进行两次DFS遍历,判断情侣之间是否存在近亲关系。示例代码展示了C++实现的详细过程,包括数据结构的选择和搜索策略。
摘要由CSDN通过智能技术生成

这一题测试数据较长,
实际上题目思路很简单,我们可以将其进行抽象成图的结构,
这样我们就可以用邻接链表来保存祖先的节点即可,最后利用dfs,将A的祖先标记,然后再利用dfs,检查是否已经有标记的,
如果有,那就代表在4代之内通婚,否则没有通婚,
所以dfs结束的条件就是这个搜索深度是否超过4代,
所以dfs搜索的状态就是(x,num);当前正在访问的节点,当前搜索的深度

题解
和L2-030冰岛人类似,但有的点比他容易
实现的话,很容易
数据结构:有父,有母,说明这不是一个普通的树
更像是一种图,肯定还是用存图的方法来存他
然后,利用BFS或者DFS进行搜索

  1. 可以把5代之内的祖先都找到,然后两个循环对比
  2. 利用BFS同时进行查找,在找的过程中进行标记,如果在查找的时候,发现他的一个祖先已经被查过了,这样肯定是有共同祖先
    题目说的是“两个人最近的共同祖先如果在五代以内”
    这一点要比冰岛人简单,冰岛人有可能一方是5代之内,另一方出5代…
    很坑的是,这样正常做下来,就17分
    有可能数据中的父母也被查询到,这个时候判断的是性别
    不能只记录自己的性别

对于查找异性情侣是否存在近亲关系,我的思路很简单,先将其中一个人五代之内与他有亲属关系的人全部做标记,在查找另一个人五代之内的亲属,如果亲属中有人带有标记,说明这对情侣不能结婚,反之可以结婚。
方法:用二维数组或vector容器存关系图,然后对两个人深搜或广搜找出五服里所有亲属。
以第七对情侣为例:
在这里插入图片描述
在上面这两颗关系树中没有出现相同的亲属,所以他们没有近亲关系。输出Yes。

再以不能结婚的最后一组情侣为例:
在这里插入图片描述
在第三层中出现已经标记过的人,说明这两个人是近亲关系,输出No。

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;

vector<int >parent[100010];
char sex[100010];
int vis[100010];
int flag;

void dfs(int nums,int id){//num表示从第几代开始,从0开始(层数)//id为编号
    if(nums>=4){
        return;
    }else{
        for(int j=0;j<parent[id].size();j++){
            if(!vis[parent[id][j]]){
                vis[parent[id][j]]=1;//标记出现过的人
                dfs(nums+1,parent[id][j]);
            }else{
                flag=1;//
            }
        }
    }
    
}

// void dfs(int index,int num){//num表示从第几代开始,从0开始
//     if(num>=4)return;//超过5代直接退出(从0开始)
//     else{
//         for(int j=0;j<parent[index].size();j++){
//             if(!vis[parent[index][j]]){//[parent[index][j]编号为index的父母的编号,没被访问过
//                 vis[parent[index][j]]=1;//标记出现的人
//                 dfs(parent[index][j],num+1);
//             }else flag=1;//[parent[index][j]编号为index的父母的编号,被访问过
//         }
//     }
    
    
// }
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        int id;
        char gender;
        scanf("%d %c",&id,&gender);//尽量使用scanf来输入
        sex[id]=gender;
        
        int fid,mid;
        scanf(" %d %d",&fid,&mid);
        if(fid!=-1){
            parent[id].push_back(fid);
            sex[fid]='M';
        }
        if(mid!=-1){
            parent[id].push_back(mid);
            sex[mid]='F';
        }
    }
    int m;
    cin>>m;
    for(int i=0;i<m;i++){
        int aid,bid;
        scanf("%d %d",&aid,&bid);
        if(sex[aid]==sex[bid])
            cout<<"Never Mind"<<endl;
        else{
            memset(vis,0,sizeof(vis));
            vis[aid]=1;
            vis[bid]=1;
            
            flag=0;
            //以下标为x的元素为根节点,第一层,遍历树,将5代以内的亲戚都做上标记
            dfs(0,aid);
            //dfs(aid,0);
            //以下标为y的元素为根节点,第一层,遍历树,在5代以内遍历时遇到标记,就说明和x有5代以内的亲戚
            dfs(0,bid);
            //dfs(bid,0);
            if(flag){//flag==1---有5代以内的标记亲戚
                cout<<"No"<<endl;
            }else{
                cout<<"Yes"<<endl;
            }
        }
    }
    return 0;
}

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int maxn=100010;

char sex[maxn];//记录性别 
vector<int> parent[maxn];//存关系图 
bool vis[maxn];//标记五服以内的亲属 
bool flag;//标记情侣是否是近亲

void dfs(int index,int num){//num表示从第几代开始,从0开始
    if(num>=4)return;//超过5代直接退出(从0开始)
    else{
        for(int j=0;j<parent[index].size();j++){
            if(!vis[parent[index][j]]){//[parent[index][j]编号为index的父母的编号,没被访问过
                vis[parent[index][j]]=1;//标记出现的人
                dfs(parent[index][j],num+1);
            }else flag=1;//[parent[index][j]编号为index的父母的编号,被访问过
        }
    }
    
    
}
int main()
{
    int T;//(给的信息)总对数24
    cin>>T;
    while(T--){
        int id,f,m;
        char gender;
        scanf("%d %c",&id,&gender);//00001 M 01111 -1  
        sex[id]=gender;
        scanf(" %d %d",&f,&m);
        if(f!=-1){//-1不用保存。避免数据处理不当导致数据越界
            parent[id].push_back(f);
            sex[f]='M';
        }
        if(m!=-1){
            parent[id].push_back(m);
            sex[m]='F';
        }
    }
        cin>>T;//(需要查询的)总对数
        while(T--){
            int x,y;
            scanf("%d %d",&x,&y);
            if(sex[x]==sex[y])//同性
                cout<<"Never Mind"<<endl;
            else{
                memset(vis,0,sizeof(vis));
                vis[x]=1;
                vis[y]=1;
                flag=0;
                dfs(x,0);
                dfs(y,0);
                if(flag)//被标记过的说明这两人是近亲
                    cout<<"No"<<endl;
                else
                    cout<<"Yes"<<endl;
            }
        }
    return 0;
}

Code
#include <iostream>
#include <vector>
#include <set>
#include <queue>
using namespace std;
const int MAX_N = 1e5 + 5;
// 性别
vector<char> sex(MAX_N);
// 关系图
vector<int> people[MAX_N];

bool judge(int x, int y)
{
    queue<int> q;
    q.push(x);
    q.push(y);
    
    vector<int> v(MAX_N);
    v[x] = v[y] = 1;
    while (!q.empty())
    {
       int  e = q.front();
        if (v[e] >= 5)
            return 1;
        q.pop();
        for (auto i : people[e])
        {
            if (v[i] != 0)
                return 0;
            v[i] = v[e] + 1;
            q.push(i);
        }
    }
    return 1;
}
int main()
{
    int N;
    cin >> N;
    while (N--)
    {
        int x;
        char c;
        int a, b;
        cin >> x >> c >> a >> b;
        sex[x] = c;
        if (~a)
        {
            sex[a] = 'M';
            people[x].push_back(a);
        }
        if (~b)
        {

            sex[b] = 'F';
            people[x].push_back(b);
        }
    }
    cin >> N;
    while (N--)
    {
        int x, y;
        cin >> x >> y;
        if (sex[x] == sex[y])
            cout << "Never Mind\n";
        else
        {
            if (judge(x, y))
                cout << "Yes\n";
            else
                cout << "No\n";
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值