• 洛谷-P2814 家谱


      题目背景

    现代的人对于本家族血统越来越感兴趣。

      题目描述

    给出充足的父子关系,请你编写程序找到某个人的最早的祖先。

      输入格式

    输入由多行组成,首先是一系列有关父子关系的描述,其中每一组父子关系中父亲只有一行,儿子可能有若干行,用 #name 的形式描写一组父子关系中的父亲的名字,用 +name 的形式描写一组父子关系中的儿子的名字;接下来用 ?name 的形式表示要求该人的最早的祖先;最后用单独的一个 $ 表示文件结束。

      输出格式

    按照输入文件的要求顺序,求出每一个要找祖先的人的祖先,格式为:本人的名字 + 一个空格 + 祖先的名字 + 回车。

      输入输出样例

    输入 #1

    输出 #1

    #George

    +Rodney

    #Arthur

    +Gareth

    +Walter

    #Gareth

    +Edward

    ?Edward

    ?Walter

    ?Rodney

    ?Arthur

    $

    Edward Arthur

    Walter Arthur

    Rodney George

    Arthur Arthur

     

      说明/提示

    规定每个人的名字都有且只有 6 个字符,而且首字母大写,且没有任意两个人的名字相同。最多可能有1e3 组父子关系,总人数最多可能达到 5e4人,家谱中的记载不超过 30 代。

      题目分析

    1 #为父亲,+为#的儿子。

    2 也就是说,+对应的节点内要存储#的位置,此处分为两种情况:

    1. #是新的,在上面案例中没有。此时用一个新的节点放#就可以,之后的+元素直接存储这个新位置的信息。
    2. #是旧的,在上面案例中出现过。那么之后的+元素需要存储它第一次位置信息。

    3 把?单独存到一个新的数组中,然后挨个去遍历它在集合中的位置,再通过集合去找它的祖宗输出即可。

      可行代码

     

    #include <iostream>
    #include <string.h>
    using namespace std;
    
    // const int MAX = 5e4 + 5;
    const int MAX = 20;
    struct {
        char name[10];
        int parent;
    } family[MAX]; // 个人信息结构体,集合用这个数组表示
    
    void init(int len) {
        for (int i = 0; i < len; i++)
            family[i].parent = i;
        return;
    }
     
    int find(int aim) {
        if (family[aim].parent != aim)
            aim = find(family[aim].parent);
        return aim;
    }
    
    void merge(int a, int b) {
        a = find(a), b = find(b);
        if (a != b)
            family[b].parent = a;
        return;
    }
    
    int search(char *str, int len) {
        char *p = str;
        p++;
        int i;
        for (i = 0; i < len; i++) {
            char *q = family[i + 1].name;
            q++;// 输入时输入了前缀+#?,这里排除
            if (strcmp(p, q) == 0)
                break;
        }
        if (i == len)
            return -1;
        return i + 1;
    }
    
    int main() {
        int cnt = 1, count = 0;
        int FindParent[MAX];
        init(MAX);
        while (1) { // 输入数组,并建立集合
            int TempParent;
            cin >> family[cnt].name;
            if (*(family[cnt].name) == '$')
                break;
            else if (*(family[cnt].name) == '#') {
                int loc = search(family[cnt].name, cnt); // 判断之前是否出现过
                if (loc != -1 && loc != cnt) {
                    TempParent = loc;
                    continue;
                } 
                family[cnt].parent = cnt;
                TempParent = cnt;// 后面的+元素,都是它的孩子
            } else if (*(family[cnt].name) == '+') {
                family[cnt].parent = TempParent;
                int loc = search(family[cnt].name, cnt);// 出现过不能计入总数
                if (loc != -1 && loc != cnt) {
                    family[loc].parent = TempParent;
                    --cnt;
                }
            } else if (*(family[cnt].name) == '?') {
                int loc = search(family[cnt].name, cnt);// ?元素,不计入集合
                FindParent[count++] = loc;
                cnt--;
            }
            cnt++;
        } // while end
        for (int i = 0; i < count; i++) {
            int loc = search(family[FindParent[i]].name, cnt);// 找到元素再集合中的位置
            int ancestor = find(loc); 
            char *p = family[FindParent[i]].name;
            char *q = family[ancestor].name;// 找父亲
            p++, q++;// 排除前缀+#
            cout << p << ' ' << q << endl;
        }
        return 0;
    }

    END 感谢rueader's阅读,洛谷题目链接:P2814 家谱 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

  • 相关阅读:
    怎样使android的view动画循环弹动
    QQ消息99+形成--第三方开源--BezierView
    自定义Toast的显示效果
    (转载)实现QQ侧滑边栏
    图片加载与缓存利器(自动缓存)--第三方开源-- Glide
    多层级Spinner列表选项实时更新树形层级(选择城市)
    android任意view爆炸效果--第三方开源--ExplosionField
    TextView字符串波浪式跳动--第三方开源---JumpingBeans
    简单Spinner
    【前端】JavaScript入门学习
  • 原文地址:https://www.cnblogs.com/kirk-notes/p/15001382.html
Copyright © 2020-2023  润新知