【PTA数据结构 | C语言版】家谱处理

本专栏持续输出数据结构题目集,欢迎订阅。

文章目录

题目

人类学研究对于家族很感兴趣,于是研究人员搜集了一些家族的家谱进行研究。实验中,使用计算机处理家谱。为了实现这个目的,研究人员将家谱转换为文本文件。下面为家谱文本文件的实例:

LaoLi
  DaLi
    XiaoMing
    XiaoHong
  ErLi
    MeiMei

家谱文本文件中,每一行包含一个人的名字。第一行中的名字是这个家族最早的祖先。家谱仅包含最早祖先的后代,而他们的丈夫或妻子不出现在家谱中。每个人的子女比父母多缩进 2 个空格。以上述家谱文本文件为例,LaoLi 是这个家族最早的祖先,他有两个孩子 DaLi 和 ErLi,DaLi 有两个孩子 XiaoMing 和 XiaoHong,ErLi 只有一个孩子 MeiMei。

在实验中,研究人员还收集了家庭文件,并提取了家谱中有关两个人关系的陈述语句。下面为家谱中关系的陈述语句实例:

LaoLi is the parent of DaLi (老李与大李是父子关系)
DaLi is a sibling of ErLi  (大李与二李是兄弟姊妹关系)
MeiMei is a descendant of DaLi (梅梅是大李的后代)

研究人员需要判断每个陈述语句是真还是假,请编写程序帮助研究人员判断。

输入格式:
输入第 1 行给出 2 个正整数 n(2≤n≤100)和 m(≤100),其中 n 为家谱中名字的数量,m 为家谱中陈述语句的数量。

接下来输入的每行不超过 70 个字符。名字的字符串由不超过 10 个英文字母组成。在家谱中的第一行给出的名字前没有缩进空格。家谱中的其他名字至少缩进 2 个空格,即他们是家谱中最早祖先(第一行给出的名字)的后代,且如果家谱中一个名字前缩进 k 个空格,则下一行中名字至多缩进 k+2 个空格。

在一个家谱中同样的名字不会出现两次,且家谱中没有出现的名字不会出现在陈述语句中。每句陈述语句格式如下(括号内为解释文字,不是格式的一部分),其中 X 和 Y 为家谱中的不同名字:

X is a child of Y (X是Y的孩子)
X is the parent of Y (X是Y的父母)
X is a sibling of Y (X是Y的兄弟姊妹)
X is a descendant of Y (X是Y的后代)
X is an ancestor of Y (X是Y的祖先)

输出格式:
对于测试用例中的每句陈述语句,如果陈述为真,在一行中输出 True;如果陈述为假,在一行中输出 False。

输入样例:

6 5
LaoLi
  DaLi
    XiaoMing
    XiaoHong
  ErLi
    MeiMei
DaLi is a child of LaoLi
DaLi is an ancestor of XiaoHong
DaLi is a sibling of ErLi
ErLi is the parent of XiaoMing
LaoLi is a descendant of XiaoHong

输出样例:
True
True
True
False
False

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX_N 100
#define MAX_NAME_LENGTH 11
#define MAX_LINE_LENGTH 71

typedef struct {
    char name[MAX_NAME_LENGTH];
    int indent;
    int parent;  // 父节点索引
} Person;

Person people[MAX_N];

// 查找名字对应的索引
int find_index(const char *name) {
    for (int i = 0; i < MAX_N; i++) {
        if (strcmp(people[i].name, name) == 0) {
            return i;
        }
    }
    return -1;
}

// 判断child是否是parent的后代
int is_descendant(int child, int parent) {
    while (child != -1) {
        if (child == parent) return 1;
        child = people[child].parent;
    }
    return 0;
}

int main() {
    int n, m;
    scanf("%d %d", &n, &m);
    getchar();  // 消耗换行符
    
    // 读取家谱
    for (int i = 0; i < n; i++) {
        char line[MAX_LINE_LENGTH];
        fgets(line, MAX_LINE_LENGTH, stdin);
        
        // 计算缩进
        int indent = 0;
        while (line[indent] == ' ') indent++;
        
        // 提取名字
        char *name_start = line + indent;
        int len = strlen(name_start);
        if (name_start[len - 1] == '\n') {
            name_start[len - 1] = '\0';
        }
        
        // 保存人物信息
        strcpy(people[i].name, name_start);
        people[i].indent = indent;
        
        // 确定父节点
        if (i == 0) {  // 第一个人是根节点
            people[i].parent = -1;
        } else {
            // 向上查找第一个缩进小于当前缩进的节点
            int parent = i - 1;
            while (parent >= 0 && people[parent].indent >= indent) {
                parent--;
            }
            people[i].parent = parent;
        }
    }
    
    // 处理陈述
    for (int i = 0; i < m; i++) {
        char line[MAX_LINE_LENGTH];
        fgets(line, MAX_LINE_LENGTH, stdin);
        
        char X[MAX_NAME_LENGTH], Y[MAX_NAME_LENGTH];
        char relation[20];
        
        // 解析陈述
        if (sscanf(line, "%10s is a child of %10s", X, Y) == 2) {
            strcpy(relation, "child");
        } else if (sscanf(line, "%10s is the parent of %10s", X, Y) == 2) {
            strcpy(relation, "parent");
        } else if (sscanf(line, "%10s is a sibling of %10s", X, Y) == 2) {
            strcpy(relation, "sibling");
        } else if (sscanf(line, "%10s is a descendant of %10s", X, Y) == 2) {
            strcpy(relation, "descendant");
        } else if (sscanf(line, "%10s is an ancestor of %10s", X, Y) == 2) {
            strcpy(relation, "ancestor");
        } else {
            printf("False\n");
            continue;
        }
        
        // 查找名字对应的索引
        int x_idx = find_index(X);
        int y_idx = find_index(Y);
        
        // 判断陈述真假
        int result = 0;
        if (strcmp(relation, "child") == 0) {
            result = (people[x_idx].parent == y_idx);
        } else if (strcmp(relation, "parent") == 0) {
            result = (people[y_idx].parent == x_idx);
        } else if (strcmp(relation, "sibling") == 0) {
            result = (people[x_idx].parent == people[y_idx].parent && x_idx != y_idx);
        } else if (strcmp(relation, "descendant") == 0) {
            result = is_descendant(x_idx, y_idx);
        } else if (strcmp(relation, "ancestor") == 0) {
            result = is_descendant(y_idx, x_idx);
        }
        
        printf("%s\n", result ? "True" : "False");
    }
    
    return 0;
}    
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋说

感谢打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值