本专栏持续输出数据结构题目集,欢迎订阅。
题目
人类学研究对于家族很感兴趣,于是研究人员搜集了一些家族的家谱进行研究。实验中,使用计算机处理家谱。为了实现这个目的,研究人员将家谱转换为文本文件。下面为家谱文本文件的实例:
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;
}
1834

被折叠的 条评论
为什么被折叠?



