hihocoder #1069 : 最近公共祖先·三(ds+rmq-st)联合求解

1 篇文章 0 订阅

题目来源:https://hihocoder.com/problemset/problem/1069?sid=1352614
结题思路:
这里题目就不给大家粘贴了,需要看原题目请点击网址,直接给给大家讲解思路把。
首先明确一点这是一道LCA类问题,解题步骤如下:

  1. 解题步骤

1.深度优先探索,得出整个探索过程的路径数组,同时在探索过程中存储下族谱树内每个成员的深度值以及最后一次出现在路径数组中的位置,这都可以完全定义在一个结构体中,这道题我看过其他人的解题过程,使用了太多数组,显得代码非常难以理解,不推荐大家使用。
结构体结构如下:

const int MAX_N = 100010;

struct Node_tree
{
    string name; // 族谱树中成员的名称
    int floor;   // 表示族谱树中,该位成员所处的层数
    int la_pos;  // 深度优先探索的时候,最后一次出现的位置
    vector<struct Node_tree*> children; // 存储直系子类成员
}fam[MAX_N];
string trav_fam[MAX_N];            // 存储族谱树的深度优先探索访问顺序
map<string, int> fam_pos;          // 辅助族谱树的创建 name 映射 fam数组中的位置
int travt_size = 0, dp[MAX_N][20]; // dp数组用于对获取得到的trav_fam[i]数组进行算法RMQ-ST操作

深度优先探索代码如下:

void ds_famTree(int pos, int floor)
{
    fam[pos].la_pos = travt_size;           // 存储深度优先探索过程中最后一次出现的位置,这个值肯定是不断在更新的
    trav_fam[travt_size++] = fam[pos].name; // 存储探索过程中所经过的结点成员名称

    if (0 == fam[pos].children.size())      // 到达叶子结点,存储层数,返回非叶子结点
    {
        fam[pos].floor = floor; // 记录层数
        return;
    }

    fam[pos].floor = floor; // 记录层数
    for (auto son : fam[pos].children)
    {
        ds_famTree(fam_pos[son->name], floor + 1);

        fam[pos].la_pos = travt_size;
        trav_fam[travt_size++] = fam[pos].name;
    }

}

经历了深度优先探索之后,得到数组trav_fam[i],使用rmq-st算法将其预处理一下(也就是线段树算法),然后获取每次需要查询的区间,输出查询结果。

void RMQ()
{
    int N = travt_size - 1;       // 探索过程中的路径个数
    for (int i = 1; i <= N; i++) // dp数组中存储当前区间中所在层数最小的结点在fam族谱数组中的位置值
    {
        dp[i][0] = fam_pos[trav_fam[i]];
    }


    for (int j = 1; (1<<j) < N; j++)
    {
        for (int i = 1; i + (1 << j) -1 <= N; i++)
        {
            int a = dp[i][j - 1], b = dp[i + (1 << (j - 1))][j - 1];
            dp[i][j] = fam[a].floor < fam[b].floor ? a : b;
        }
    }
}

最后便是输出结果:

void query(int le, int ri)
{
    int k = 0;
    while ((1 << (k+1)) <= (ri - le + 1)) // 获取数组dp[i,j]中的j值
    {
        k++;
    }

    int a = dp[le][k], b = dp[ri - (1 << k) + 1][k];
    cout << (fam[a].floor < fam[b].floor ? fam[a].name : fam[b].name) << endl;
}

void get_res()
{
    string per1, per2;
    int M;

    cin >> M;
    for (int i = 0; i < M; i++)
    {
        cin >> per1 >> per2;

        if (per1 == per2)
        {
            cout << per1 << endl;
            continue;
        }

        int p1 = min(fam[fam_pos[per1]].la_pos,fam[fam_pos[per2]].la_pos);
        int p2 = max(fam[fam_pos[per1]].la_pos, fam[fam_pos[per2]].la_pos);

        query(p1, p2);
    }
}

最终的完整代码如下:

#include <cstdio>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <iostream>

using std::string;
using std::vector;
using std::map;
using std::cin;
using std::cout;
using std::endl;
using std::max;
using std::min;

const int MAX_N = 100010;

struct Node_tree
{
    string name; // 族谱树中成员的名称
    int floor;   // 表示族谱树中,该位成员所处的层数
    int la_pos;  // 深度优先探索的时候,最后一次出现的位置
    vector<struct Node_tree*> children; // 存储直系子类成员
}fam[MAX_N];

string trav_fam[MAX_N];            // 存储族谱树的深度优先探索访问顺序
map<string, int> fam_pos;          // 辅助族谱树的创建 name 映射 fam数组中的位置
int travt_size = 0, dp[MAX_N][20]; // dp数组用于对获取得到的trav_fam[i]数组进行算法RMQ-ST操作

void init()
{
    int N;
    string fa, son;

    scanf("%d", &N);

    int per_size = 1;
    for (int i = 0; i < N; i++)
    {
        cin >> fa >> son;

        if (0 == fam_pos[fa])
        {
            fam_pos[fa] = per_size; // 存储在数组 fam 中的标位置
            fam[per_size].name = fa;
            per_size++;
        }

        if (0 == fam_pos[son])
        {
            fam_pos[son] = per_size;
            fam[per_size].name = son;
            per_size++;
        }

        fam[fam_pos[fa]].children.emplace_back(&fam[fam_pos[son]]);
    }
}

void ds_famTree(int pos, int floor)
{
    fam[pos].la_pos = travt_size;           // 存储深度优先探索过程中最后一次出现的位置,这个值肯定是不断在更新的
    trav_fam[travt_size++] = fam[pos].name; // 存储探索过程中所经过的结点成员名称

    if (0 == fam[pos].children.size())      // 到达叶子结点,存储层数,返回非叶子结点
    {
        fam[pos].floor = floor; // 记录层数
        return;
    }

    fam[pos].floor = floor; // 记录层数
    for (auto son : fam[pos].children)
    {
        ds_famTree(fam_pos[son->name], floor + 1);

        fam[pos].la_pos = travt_size;
        trav_fam[travt_size++] = fam[pos].name;
    }

}

void RMQ()
{
    int N = travt_size - 1;
    for (int i = 1; i <= N; i++) // dp数组中存储当前区间中所在层数最小的结点在fam族谱数组中的位置值
    {
        dp[i][0] = fam_pos[trav_fam[i]];
    }


    for (int j = 1; (1<<j) < N; j++)
    {
        for (int i = 1; i + (1 << j) -1 <= N; i++)
        {
            int a = dp[i][j - 1], b = dp[i + (1 << (j - 1))][j - 1];
            dp[i][j] = fam[a].floor < fam[b].floor ? a : b;
        }
    }
}

void query(int le, int ri)
{
    int k = 0;
    while ((1 << (k+1)) <= (ri - le + 1)) // 获取数组dp[i,j]中的j值
    {
        k++;
    }

    int a = dp[le][k], b = dp[ri - (1 << k) + 1][k];
    cout << (fam[a].floor < fam[b].floor ? fam[a].name : fam[b].name) << endl;
}

void get_res()
{
    string per1, per2;
    int M;

    cin >> M;
    for (int i = 0; i < M; i++)
    {
        cin >> per1 >> per2;

        if (per1 == per2)
        {
            cout << per1 << endl;
            continue;
        }

        int p1 = min(fam[fam_pos[per1]].la_pos,fam[fam_pos[per2]].la_pos);
        int p2 = max(fam[fam_pos[per1]].la_pos, fam[fam_pos[per2]].la_pos);

        query(p1, p2);
    }
}

int main(void)
{
    // 1.创建族谱树
    init();

    // 2.深度优先探索族谱树,得出探寻轨迹数组
    ds_famTree(1, 1);

    // 3.使用RMQ-ST算法将获取的路径字数组进行预处理
    RMQ();

    // 4.获取询问,输出结果
    get_res();

    getchar();
    getchar();
    return 0;
}

在此声明:转载请注明出处!!!
同时,题目来源于hihocoder网站,博主在此声明仅使用本题用于代码交流学习,不将用于任何商业用途。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值