• hiho_1069_最近公共祖先3


    题目

        给出一棵家族树,树上的节点可以由名字唯一标识。给出若干个查询,查询的内容为两个名字,结果为两个名字的最近公共祖先。 
    题目链接: 最近公共祖先

    分析

        在线的RMQ + LCA 算法,先用dfs将树遍历一遍,每次到达一个节点(无论是从父节点到达还是从它的某个子节点返回),都记录下来该节点id,同时记录下来该节点所在的深度。这样一次dfs之后,将树变成两个数组(一个为按照顺序遍历得到的深度数组A,一个是按照顺序遍历得到的节点id数组B)。 
        要求两个节点的最近公共祖先,可以分别找到两个节点最后被访问时刻在A中的位置p1和p2,然后[p1, p2]区间内的最小深度值所在的位置就是就是这两个节点的最近公共祖先在访问顺序数组A中的位置。 
    根据数组B,得到LCA的节点id。 
        求区间最小值,使用RMQ_ST算法。

    实现

    #include<iostream>
    #include<string.h>
    #include<queue>
    #include<cmath>
    #include<unordered_map>
    #include<unordered_set>
    #include<string>
    #include<vector>
    using namespace std;
    const int inf = 1 << 29;
    const int kMax = 200005;
    unordered_map<string, int> gName2node;
    unordered_map<int, string> gNode2name;
    int gVisitNode[2 * kMax];	//按照访问次序,节点的排列
    int gVisitDepth[2 * kMax];	//按照访问次序,节点的深度
    int gNodeLastVisitSeq[kMax]; //节点最后一次被访问的序号
    int dp[2*kMax][21][2];
    struct Edge{
    	int to;
    	int next;
    };
    Edge gEdges[kMax];
    int gEdgeIndex;
    int gHead[kMax];
    
    //从根节点出发,按照dfs方式遍历整个树,将每一步遍历到的节点放到 visit_seq 中
    void Dfs(int node, int& cur_seq, int depth){
    	gVisitDepth[cur_seq] = depth; //记录当前深度,用于后续的区间最小查询
    	gNodeLastVisitSeq[node] =  cur_seq;
    	gVisitNode[cur_seq++] = node; //进入node节点
    	for (int e = gHead[node]; e != -1; e = gEdges[e].next){
    		int v = gEdges[e].to;
    		Dfs(v, cur_seq, depth+1);
    		gVisitDepth[cur_seq] = depth;
    		gNodeLastVisitSeq[node] = cur_seq;
    		gVisitNode[cur_seq++] = node; //离开node节点
    	}
    }
    
    
    void InsertEdge(int u, int v){
    	int e = gEdgeIndex++;
    	gEdges[e].to = v;
    	gEdges[e].next = gHead[u];
    	gHead[u] = e;
    }
    
    void Init(int n){
    	gEdgeIndex = 0;
    	memset(gEdges, -1, sizeof(gEdges));
    	memset(gHead, -1, sizeof(gHead));
    	memset(dp, 0, sizeof(dp));
    }
    
    void AddPerson(string person){
    	if (gName2node.find(person) == gName2node.end()){
    		int node = gName2node.size();
    		gName2node[person] = node;
    		gNode2name[node] = person;
    	}
    }
    inline int min(int a, int b){
    	return a < b ? a : b;
    }
    int main(){
    	int n, n1, n2;
    	string person1, person2;
    	cin >> n;
    	Init(n);
    	for (int i = 0; i < n; i++){
    		cin >> person1 >> person2;
    		AddPerson(person1);
    		AddPerson(person2);
    		n1 = gName2node[person1];
    		n2 = gName2node[person2];
    		InsertEdge(n1, n2);
    	}
    	int cur_seq = 0;
    	Dfs(0, cur_seq, 0);
    	//树的遍历形成的数组,长度为cur_seq
    	for (int i = 1; i <= cur_seq; i++){
    		dp[i][0][0] = gVisitDepth[i - 1];
    		dp[i][0][1] = gVisitNode[i - 1];
    	}
    	for (int j = 0; j <= 20; j++){
    		dp[0][j][0] = inf;
    	}
    	int len = log2(cur_seq);
    	for (int j = 1; j <= len; j++){
    		for (int i = 1; i + (1 << j) - 1 <= cur_seq; i++){
    			if (dp[i][j - 1][0] < dp[i + (1 << (j - 1))][j - 1][0]){
    				dp[i][j][0] = dp[i][j - 1][0];
    				dp[i][j][1] = dp[i][j - 1][1];
    			}
    			else{
    				dp[i][j][0] = dp[i + (1 << j - 1)][j - 1][0];
    				dp[i][j][1] = dp[i + (1 << j - 1)][j - 1][1];
    			}
    		}
    	}
    	int m;
    	cin >> m;
    	for (int i = 0; i < m; i++){
    		cin >> person1 >> person2;
    		n1 = gNodeLastVisitSeq[gName2node[person1]] + 1;
    		n2 = gNodeLastVisitSeq[gName2node[person2]] + 1;
    		if (n1 > n2)
    			swap(n1, n2);
    		int k = log2(n2 - n1 + 1);
    		int min_depth_node;
    		if (dp[n1][k][0] < dp[n2 - (1 << k) + 1][k][0]){
    			min_depth_node = dp[n1][k][1];
    		}
    		else{
    			min_depth_node = dp[n2 - (1 << k) + 1][k][1];
    		}
    		cout << gNode2name[min_depth_node] << endl;
    	}
    	return 0;
    }
    
  • 相关阅读:
    Git小结---So far.......
    Git命令之:git push
    Git详解之:Git分支
    使用Dom4j解析XML
    crm客户资源显示控制
    crm---本项目的权限控制模式
    在mybatis中,在列表分页查询过程中造成集合属性数据丢失的问题
    在easyui中解决使west和center为1:1,并且拖动窗口时能够自适应变化
    在springmvc中使用requestContextListener获取全部的request对象
    HTML 元素标签语义化及使用场景
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/5544052.html
Copyright © 2020-2023  润新知