• 家谱


    题目背景

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

    题目描述

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

    输入输出格式

    输入格式:

     

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

     

    输出格式:

     

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

    输入输出样例

    输入样例#1: 
    #George
    +Rodney
    #Arthur
    +Gareth
    +Walter
    #Gareth
    +Edward
    ?Edward
    ?Walter
    ?Rodney
    ?Arthur
    $
    输出样例#1: 
    Edward Arthur
    Walter Arthur
    Rodney George
    Arthur Arthur

    说明

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

    其实不看数据的格式的话,这道题就是一道不难的并查集题。但就因为是字符串并查集,才为这道题增添了不少难度。

    有两种做法,一是用hash编码在解码,而是比较简洁,用map。

    第一种用hash的方法不赖,因为只有6个字符,所以最大的hash值无论如何都不会超过1000,也无需进行mod处理。但不太好办的是解码,因为输出的是人名,并不是hash值。所以每次输出还需O(n)的时间来解码,复杂度不是很好。

    第二种用map,这也是我个人比较喜欢的做法。直接把数组的下标和值全都定义成string类型的,就会非常方便,和普通的并查集几乎没什么不同。

    但有一点需要注意,就是初始化。因为是迭代器,所以不能直接一个大for跑完把p[i] = i,况且每一个 i 还是一个字符串,不知有多少种情况。

    所以采用动态初始化,第一次遇到某个字符串 i 的时候再 p[i] = i。所以还要再开一个map充当vis数组。虽然复杂度多了一个log,但以为数据最大5e4,没什么影响。

    给一个用map的方法。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<string>
     6 #include<map>
     7 using namespace std;
     8 const int maxn = 5e4 + 5;
     9 map<string, string> p;
    10 string Find(string x)
    11 {
    12     return x == p[x] ? x : p[x] = Find(p[x]);
    13 }
    14 void merge(string x, string y)        //规定x就是y的父亲 
    15 {
    16     string px = Find(x), py = Find(y);
    17     if(px == py) return;
    18     else {p[py] = px; return;}        //y的祖先py指向x的祖先px 
    19 }
    20 string n;
    21 map<string, bool> vis;
    22 int main()
    23 {
    24     while(1)
    25     {
    26         char c; cin >> c;
    27         if(c == '$') break;
    28         string x; cin >> x;
    29         if(c == '#') 
    30         {
    31             if(!vis[x]) {p[x] = x; vis[x] = 1;}        //初始化 
    32             n = x;            //用string就比较方便,可以直接赋值 
    33         }
    34         else if(c == '+') 
    35         {
    36             if(!vis[x]) {p[x] = x; vis[x] = 1;}     //初始化 
    37             merge(n, x);
    38         }
    39         else cout << x << ' ' << Find(x) << endl;
    40     }
    41     return 0;
    42 }
  • 相关阅读:
    15 个 Android 通用流行框架大全
    RecycleViewScrollHelper--RecyclerView滑动事件检测的辅助类
    RecyclerView的滚动事件分析
    Fresco框架SimpleDraweeView控件的简单使用
    Calling C++ code from C# z
    DevExpress控件使用小结 z
    DevExpress 中根据数据库字典动态生成卡式菜单 z
    EasyHook远注简单监控示例 z
    dll打包进需要发布的exe z
    put a ContextMenu into the header of a TabPage z
  • 原文地址:https://www.cnblogs.com/mrclr/p/8878760.html
Copyright © 2020-2023  润新知