[关闭]
@lunar 2016-03-30T22:27:24.000000Z 字数 3978 阅读 1210

HiHo一下 Week15 最近公共祖先 离线算法 + STL::vector初探

HiHo


描述

上上回说到,小Hi和小Ho用非常拙劣——或者说粗糙的手段山寨出了一个神奇的网站,这个网站可以计算出某两个人的所有共同祖先中辈分最低的一个是谁。远在美国的他们利用了一些奇妙的技术获得了国内许多人的相关信息,并且搭建了一个小小的网站来应付来自四面八方的请求。

但正如我们所能想象到的……这样一个简单的算法并不能支撑住非常大的访问量,所以摆在小Hi和小Ho面前的无非两种选择:

其一是购买更为昂贵的服务器,通过提高计算机性能的方式来满足需求——但小Hi和小Ho并没有那么多的钱;其二则是改进他们的算法,通过提高计算机性能的利用率来满足需求——这个主意似乎听起来更加靠谱。

于是为了他们第一个在线产品的顺利运作,小Hi决定对小Ho进行紧急训练——好好的修改一番他们的算法。

而为了更好的向小Ho讲述这个问题,小Hi将这个问题抽象成了这个样子:假设现小Ho现在知道了N对父子关系——父亲和儿子的名字,并且这N对父子关系中涉及的所有人都拥有一个共同的祖先(这个祖先出现在这N对父子关系中),他需要对于小Hi的若干次提问——每次提问为两个人的名字(这两个人的名字在之前的父子关系中出现过),告诉小Hi这两个人的所有共同祖先中辈分最低的一个是谁?

输入

每个测试点(输入文件)有且仅有一组测试数据。

每组测试数据的第1行为一个整数N,意义如前文所述。

每组测试数据的第2~N+1行,每行分别描述一对父子关系,其中第i+1行为两个由大小写字母组成的字符串Father_i, Son_i,分别表示父亲的名字和儿子的名字。

每组测试数据的第N+2行为一个整数M,表示小Hi总共询问的次数。

每组测试数据的第N+3~N+M+2行,每行分别描述一个询问,其中第N+i+2行为两个由大小写字母组成的字符串Name1_i, Name2_i,分别表示小Hi询问中的两个名字。

对于100%的数据,满足N<=10^5,M<=10^5, 且数据中所有涉及的人物中不存在两个名字相同的人(即姓名唯一的确定了一个人),所有询问中出现过的名字均在之前所描述的N对父子关系中出现过,第一个出现的名字所确定的人是其他所有人的公共祖先。

输出

对于每组测试数据,对于每个小Hi的询问,按照在输入中出现的顺序,各输出一行,表示查询的结果:他们的所有共同祖先中辈分最低的一个人的名字。

样例

  1. 4
  2. Adam Sam
  3. Sam Joey
  4. Sam Micheal
  5. Adam Kevin
  6. 3
  7. Sam Sam
  8. Adam Sam
  9. Micheal Kevin
  1. Sam
  2. Adam
  3. Adam

思路

这次相比上次的LCA(最近公共祖先)问题,数据量大了很多,因此不能再用之前的算法,这次引入离线算法Tarjan。
所谓离线算法指的是收集一定量查询后一起进行输出,也就是说查询的数量对算法运行时间不会有多大影响。打个不恰当的例子,吃一口饭拉一坨翔是不是很费时间还容易菊花疼得痔疮吧,吃两顿再拉一坨就节省时间多了。ヽ(́◕◞౪◟◕‵)ノ
Tarjan算法主要使用的是并查集+后序DFS。
简单的说,建好树后进行DFS后序遍历,将树上的节点分为两类,一类是已经遍历过的称为A类,一类是没有遍历的称为B,其外还有一个节点就是当前结点,很容易发现,当前结点和任意一个已经遍历过的节点的LCA都是同一个,只要在遍历到该节点时看一下包含该节点的查询,如果另外一个节点还没遍历,那么搁置,如果已经遍历过了,就说明,二者的最近公共祖先就是A类的LCA。每当遍历完一个节点,就将其置为遍历过,且将其集合并到父节点上。
我语文不好表达得不清楚,这个博客讲得还好。。。
ps:这里要讲一下C++里面stl vector的用法。vector是一个动态容量的顺序容器。需要引用头文件<vector>
声明:template <class T> v
示例:

  1. vector<int> v; //声明 v
  2. vector<int> u(10); //声明u并初始化空间为10
  3. u[5] = 5; //因为之前初始化过空间过,所以可以直接访问u[5]
  4. u.at(5) = 5; //这句和上面的功能相同,但是at函数会检查容量,如果爆了会报 out_of_range 错误
  5. u.push_back(12); //这行代码在u的最后插上一个为12的值,这里虽然只有u[5]有赋值,但是由于之前初始化空间10了,所以会将12赋给第11个元素也就是u[10]
  6. cout << u.size()<<" "<<u.capacity(); // 会输出"11 20",由于vector动态增长,每次不够用了会自动开辟10空间,所以capacity为20,但是最后一个存有数据的元素为第11个,所以size为11。
  7. v.push_back(12); //v一直是空的,所以这里等价于v[0]=12
  8. u.clear(); //清除
  9. //删除指定位置元素 这里用到迭代器
  10. vector<int>::iterator it;
  11. it = u.begin();
  12. u.erase(it); //这样就删除了u的第一个元素

代码

  1. #include<iostream>
  2. #include<string>
  3. #include<map>
  4. #include<vector>
  5. #define max 100005
  6. using namespace std;
  7. std::map<string,int> header;
  8. std::vector<int> tree[max];
  9. int index=0;
  10. string name[max];
  11. std::map<std::pair<int,int>,string> l;
  12. int set[max];
  13. int ancestor[max];
  14. void buildTree(){
  15. int n;
  16. string a,b;
  17. std::map<string,int>::iterator idIta;
  18. std::map<string,int>::iterator idItb;
  19. cin >>n;
  20. while(n--){
  21. cin >> a;
  22. cin >> b;
  23. idIta = header.find(a);
  24. if(idIta == header.end()) {
  25. name[index] = a;
  26. header[a] = index++;
  27. idIta = header.find(a);
  28. }
  29. idItb = header.find(b);
  30. if(idItb == header.end()) {
  31. name[index] = b;
  32. header[b] = index++;
  33. idItb = header.find(b);
  34. }
  35. tree[idIta->second].push_back(idItb->second);
  36. }
  37. }
  38. struct point{
  39. int x;
  40. int y;
  41. }temp;
  42. std::vector<int> query[max];
  43. std::vector<point> q;
  44. void readQuery()
  45. {
  46. int n;
  47. string a,b;
  48. cin >> n;
  49. std::map<string,int>::iterator idIta;
  50. std::map<string,int>::iterator idItb;
  51. for(int i=0;i<n;i++){
  52. cin >> a;
  53. cin >> b;
  54. idIta = header.find(a);
  55. idItb = header.find(b);
  56. query[idIta->second].push_back(idItb->second);
  57. query[idItb->second].push_back(idIta->second);
  58. if(idIta->second<idItb->second)
  59. {temp.x = idIta->second;temp.y = idItb->second;}
  60. else {temp.x = idItb->second;temp.y = idIta->second;}
  61. q.push_back(temp);
  62. }
  63. }
  64. void initSet(){
  65. for (int i=0;i<index;i++)
  66. {
  67. set[i] = i;
  68. ancestor[i] = i;
  69. }
  70. }
  71. int findSet(int x){
  72. if(x != set[x]) set[x] = findSet(set[x]);
  73. return set[x];
  74. }
  75. void unionSet(int x,int y){
  76. int xF = findSet(x);
  77. int yF = findSet(y);
  78. if(xF == yF) return;
  79. else set[yF] = xF;
  80. }
  81. bool flag[max];
  82. void DFS(int x){
  83. for (int i=0;i<tree[x].size();i++){
  84. DFS(tree[x][i]);
  85. unionSet(x,tree[x][i]);
  86. ancestor[findSet(x)] = x;
  87. }
  88. flag[x] = 1;
  89. for (int i=0;i<query[x].size();i++){
  90. if(flag[query[x][i]]){
  91. string t = name[ancestor[findSet(query[x][i])]];
  92. if(x<query[x][i]){
  93. l[pair<int,int>(x,query[x][i])] = t;
  94. }else{
  95. l[pair<int,int>(query[x][i],x)] = t;
  96. }
  97. }
  98. }
  99. }
  100. void output(){
  101. for(int i=0;i<q.size();i++){
  102. std::map<pair<int,int>,string>::iterator itt;
  103. itt = l.find(pair<int,int>(q[i].x,q[i].y));
  104. cout <<itt->second<<endl;
  105. }
  106. }
  107. int main(){
  108. buildTree();
  109. //memset(flag, 0, sizeof(flag));
  110. initSet();
  111. readQuery();
  112. DFS(0);
  113. output();
  114. return 0;
  115. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注