【HDU 4547 CD操作】LCA问题 Tarjan算法

简介: 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4547 题意:模拟DOS下的cd命令,给出n个节点的目录树以及m次查询,每个查询包含一个当前目录cur和一个目标目录tar,返回从cur切换到tar所要使用的cd命令次数: 注意这里的cd命令是简化版,只能进行如下两种操作:   1.

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4547

题意:模拟DOS下的cd命令,给出n个节点的目录树以及m次查询,每个查询包含一个当前目录cur和一个目标目录tar,返回从cur切换到tar所要使用的cd命令次数:

注意这里的cd命令是简化版,只能进行如下两种操作:

  1. cd   ..                                        //返回父目录

  2. cd   cur\一系列目录\tar                 //由当前目录跳转到目标目录,注意中间的“一系列目录”不能包含父目录..,也就是说,自底向上必须一步步走,而自顶向下可以一步到位

思路:用Tarjan算法求LCA,处理查询时要分类讨论:

  1. if tar == lca,则res(cur, tar) = depth(cur) - depth(lca); (包含tar == cur的情况)

  2. else if cur == lca, 则res(cur, tar) = 1;

  3. 其他,则res(cur, tar) = depth(cur) - depth(lca) + 1;

这道题还有些细节问题需要处理好:

1. 每个查询要记录好目标目录是谁。因为tarjan算法是批处理的,即每完成对一个棵子树的遍历,处理其树根所涉及到的查询。为使查询处理不遗漏,我们把查询也以邻接表的形式存成双向边,然而每次处理需要知道本次查询原始的“单向边”,这样才能根据上面的分类计算结果。所以我另设了一个数组ans_tar[i]记录第 i 个查询所给定的tar。

2. 同HDU2586这道题,多个查询,注意记录查询序列号。这道题我用邻接表项query_id[r][i]记录节点r的第i个查询所持有的查询序列号,用邻接表项query_tar[r][i]记录节点r的第i个查询的目标节点。

3. 给出的目录是字符串,要用一个map<string, int>存储名称到节点号的映射关系。

 这里又了解到了map一个用法,即对[]的重载:对于map<string, int> m,如果调用一次m[s],而s在m中不存在时,会自动插入s并将它的value置为0。这个设计对于这道题很合适,可以维护全局计数变量seq_num,如果返回0的话,分发下一个序号给它即可。

  1 #include <cstdio>
  2 #include <iostream>
  3 #include <vector>
  4 #include <map>
  5 #include <string>
  6 #include <cstring>
  7 using namespace std;
  8 const int MAX_N = 100005;
  9 const int MAX_M = 100005;
 10 
 11 int vis[MAX_N];
 12 int ans[MAX_N];
 13 int ans_tar[MAX_N];//每组查询的目标目录
 14 int indeg[MAX_N];
 15 int depth[MAX_N];
 16 map<string, int> name;
 17 vector<int> G[MAX_N];//邻接表,边不带权
 18 vector<int> query_tar[MAX_N];
 19 vector<int> query_id[MAX_N];
 20 int par[MAX_N];
 21 
 22 int T;
 23 int n, m;
 24 int seq_num;//目录名的序列号,从1开始
 25 
 26 void init(){
 27     seq_num = 1;
 28     memset(vis, 0, sizeof(vis));
 29     memset(ans, 0, sizeof(ans));
 30     memset(ans_tar, 0, sizeof(ans_tar));
 31     memset(indeg, 0, sizeof(indeg));
 32     memset(depth, 0, sizeof(depth));
 33     name.clear();
 34     for(int i=0; i<MAX_N; i++){
 35         G[i].clear();
 36         query_tar[i].clear();
 37         query_id[i].clear();
 38         par[i] = i;
 39     }
 40 }
 41 int find(int x){
 42     return par[x]==x ? x : par[x] = find(par[x]);
 43 }
 44 void unite(int x, int y){
 45     x = find(x);
 46     y = find(y);
 47     if(x==y) return ;
 48     par[y] = x;
 49 }
 50 
 51 void dfs(int r, int l){
 52     //cout << "dfs " << r << endl;
 53     vis[r] = 1;
 54     depth[r] = l;
 55     for(int i=0; i<G[r].size(); i++){
 56         //cout << G[r][i] << endl;
 57         if(vis[G[r][i]]) continue;
 58         dfs(G[r][i], l+1);
 59         unite(r, G[r][i]);
 60     }
 61     for(int i=0; i<query_tar[r].size(); i++){
 62         if(!vis[query_tar[r][i]]) continue;
 63         int cur, tar;
 64         int ans_id = query_id[r][i];//这一查询所持的序列号
 65         int real_tar = ans_tar[ans_id];//这一个查询真正指定的target
 66         if(r == real_tar){//当前r是目标
 67             cur = query_tar[r][i];
 68             tar = real_tar;
 69         }else{//已访问过的那个点是目标
 70             cur = r;
 71             tar = real_tar;
 72         }
 73         //cout << "query " << cur << tar << endl;
 74         int ca = find(query_tar[r][i]);
 75         if(tar == ca) ans[ans_id] = depth[cur] - depth[ca];
 76         else if(cur == ca) ans[ans_id] = 1;
 77         else ans[ans_id] = depth[cur] - depth[ca] + 1;
 78     }
 79 }
 80 
 81 void lca(int r){
 82     //cout << "root " << r << endl;
 83     dfs(r, 0);
 84 }
 85 
 86 int main()
 87 {
 88     freopen("4547.txt", "r", stdin);
 89     scanf("%d", &T);
 90     while(T--){
 91         init();
 92         scanf("%d%d", &n, &m);
 93         for(int i=0; i<n-1; i++){
 94             string c, p;
 95             cin>>c;
 96             int u = name[c];//不存在会自动插入并置value为0
 97             if(u==0){
 98                 u = name[c] = seq_num++;
 99             }
100 
101             cin>>p;
102             int v = name[p];
103             if(v==0){
104                 v = name[p] = seq_num++;
105             }
106             G[u].push_back(v);
107             G[v].push_back(u);
108             indeg[u]++;//入度为0的是根目录
109         }
110         // for(map<string, int>::iterator iter = name.begin(); 
111         //     iter != name.end(); iter++){
112         //     cout << iter->first << iter->second << endl;
113         // }
114         for(int i=0; i<m; i++){
115             string cur, tar;
116             cin >> cur >> tar;
117             int u = name[cur];
118             int v = name[tar];
119             query_tar[u].push_back(v);
120             query_tar[v].push_back(u);
121             query_id[u].push_back(i);//tar和id是同步记录的
122             query_id[v].push_back(i);
123             ans_tar[i] = v;//为辨谁是目标目录
124         }
125         for(map<string, int>::iterator iter = name.begin(); 
126             iter != name.end(); iter++){
127             int id = iter->second;
128             if(indeg[id] == 0){
129                 lca(id);
130                 break;
131             }
132         }
133         for(int i=0; i<m; i++){
134             printf("%d\n", ans[i]);
135         }
136     }
137     return 0;
138 }
目录
相关文章
|
6月前
|
机器学习/深度学习 存储 算法
【算法基础】常数操作 时间复杂度 选择排序 冒泡排序 插入排序 位运算
【算法基础】常数操作 时间复杂度 选择排序 冒泡排序 插入排序 位运算
|
算法 Android开发 索引
LeetCode 周赛上分之旅 #44 同余前缀和问题与经典倍增 LCA 算法
学习数据结构与算法的关键在于掌握问题背后的算法思维框架,你的思考越抽象,它能覆盖的问题域就越广,理解难度也更复杂。在这个专栏里,小彭与你分享每场 LeetCode 周赛的解题报告,一起体会上分之旅。
80 0
|
5月前
|
算法 前端开发 Linux
【常用技巧】C++ STL容器操作:6种常用场景算法
STL在Linux C++中使用的非常普遍,掌握并合适的使用各种容器至关重要!
89 10
|
5月前
|
算法
数据结构和算法学习记录——二叉搜索树的插入操作、删除操作
数据结构和算法学习记录——二叉搜索树的插入操作、删除操作
32 0
|
5月前
|
算法
数据结构和算法学习记录——认识二叉搜索树及二叉搜索树的查找操作(递归以及迭代实现-查找操作、查找最大和最小元素)
数据结构和算法学习记录——认识二叉搜索树及二叉搜索树的查找操作(递归以及迭代实现-查找操作、查找最大和最小元素)
50 0
|
6月前
|
机器学习/深度学习 人工智能 运维
人工智能平台PAI 操作报错合集之请问Alink的算法中的序列异常检测组件,是对数据进行分组后分别在每个组中执行异常检测,而不是将数据看作时序数据进行异常检测吧
阿里云人工智能平台PAI (Platform for Artificial Intelligence) 是阿里云推出的一套全面、易用的机器学习和深度学习平台,旨在帮助企业、开发者和数据科学家快速构建、训练、部署和管理人工智能模型。在使用阿里云人工智能平台PAI进行操作时,可能会遇到各种类型的错误。以下列举了一些常见的报错情况及其可能的原因和解决方法。
|
6月前
|
算法 测试技术 C++
【数论】【分类讨论】【C++算法】1611使整数变为 0 的最少操作次数
【数论】【分类讨论】【C++算法】1611使整数变为 0 的最少操作次数
|
6月前
|
算法 定位技术 Python
ArcGIS中ArcMap栅格重采样操作与算法选择
ArcGIS中ArcMap栅格重采样操作与算法选择
201 1
|
6月前
|
算法 vr&ar Windows
Tarjan算法求LCA(最近公共祖先)
Tarjan算法求LCA(最近公共祖先)
40 0
|
11月前
|
算法 测试技术 C#
C++二分算法:得到子序列的最少操作次数
C++二分算法:得到子序列的最少操作次数