pku 1986 LCA算法的应用
2011-08-02 22:21
411 查看
题目大意是在一棵树中,通过提问的方式找出任意两点间的最短距离。对每颗树的提问次数可高达1W次。
分析:
此题高达4W多个点,4W多条边,还有如此高的提问次数,针对题目次数这么多,我们容易想到将每两个点的距离都找出来,以后你问一个我直接作答。
而且更容易想到的是floyd算法,可是我们注意到节点高达4W。肯定超时!
我们要注意这是一棵树,在一棵树中,两个节点通过公共祖先而互达的那条边一定是最短的。那么问题便转化为了求公共祖先。
利用LCA的离线tarjan算法,我们可以轻松的找到任意两点间的祖先,同时在深度遍历的同时,记录所有点到root的距离。最后我们只要输出
该两点离root的距离之和减去两倍的祖先到root的距离,也即:dist[a]+dist[b]-2*dist[lca(a,b)];
还有,该题是通过提问的方式来考察,我们不可能每次都运用tarjan算法找祖先,我们只有把所有问题制成链表,在第一次tarjan中便找出所有问题的祖先节点。
tarjan中,找到某两点的祖先的依据是,当前已遍历到了其中一个点,并且另一个点早也被遍历过,那么沿着任意一个节点往回找,便可以找到祖先。
证明:因为我们对每一个节点开始进行遍历的时候,都是将它做为一个独立的集合来处理,它和它所有的子孙都将被加入这个集合,这所有的都处理完之后,最后我们再决定该集合将属于谁。而我们通过该两点找祖先的事件是发生在该集合合并给其他集合之前,也就是说,他们的祖先节点的pre[x]一定是等于x的!
code
分析:
此题高达4W多个点,4W多条边,还有如此高的提问次数,针对题目次数这么多,我们容易想到将每两个点的距离都找出来,以后你问一个我直接作答。
而且更容易想到的是floyd算法,可是我们注意到节点高达4W。肯定超时!
我们要注意这是一棵树,在一棵树中,两个节点通过公共祖先而互达的那条边一定是最短的。那么问题便转化为了求公共祖先。
利用LCA的离线tarjan算法,我们可以轻松的找到任意两点间的祖先,同时在深度遍历的同时,记录所有点到root的距离。最后我们只要输出
该两点离root的距离之和减去两倍的祖先到root的距离,也即:dist[a]+dist[b]-2*dist[lca(a,b)];
还有,该题是通过提问的方式来考察,我们不可能每次都运用tarjan算法找祖先,我们只有把所有问题制成链表,在第一次tarjan中便找出所有问题的祖先节点。
tarjan中,找到某两点的祖先的依据是,当前已遍历到了其中一个点,并且另一个点早也被遍历过,那么沿着任意一个节点往回找,便可以找到祖先。
证明:因为我们对每一个节点开始进行遍历的时候,都是将它做为一个独立的集合来处理,它和它所有的子孙都将被加入这个集合,这所有的都处理完之后,最后我们再决定该集合将属于谁。而我们通过该两点找祖先的事件是发生在该集合合并给其他集合之前,也就是说,他们的祖先节点的pre[x]一定是等于x的!
code
#include<iostream> #include<string> using namespace std; //邻接表建图 typedef struct node { int v; int w; struct node *next; }node; node *link[40005]; node edge[100000]; int num; void add(int u,int v,int w) { edge[num].v=v; edge[num].w=w; edge[num].next=link[u]; link[u]=edge+num++; edge[num].v=u; edge[num].w=w; edge[num].next=link[v]; link[v]=edge+num++; } //提问链表的建立 typedef struct qnode { int v; int id; //记录第几个问题 struct qnode *next; }qnode; qnode *link1[40005]; qnode edge1[100000]; int num1; void add1(int u,int v,int id) { edge1[num1].v=v; edge1[num1].id=id; edge1[num1].next=link1[u]; link1[u]=edge1+num1++; edge1[num1].v=u; edge1[num1].id=id; edge1[num1].next=link1[v]; link1[v]=edge1+num1++; } //LCA部分 int n,m; int v[40005],pre[40005],dist[40005]; int ans[10005]; int find(int x) { if(x!=pre[x]) { pre[x]=find(pre[x]); } return pre[x]; } void tarjan(int u,int f) { pre[u]=u; //开始遍历,将该点作为一个独立的集合 for(node *p=link[u];p;p=p->next) { if(p->v==f) continue; if(!v[p->v]) { v[p->v]=1; dist[p->v]=dist[u]+p->w; tarjan(p->v,u); pre[p->v]=u; //所有子孙添加到该集合 } } for(qnode *p1=link1[u];p1;p1=p1->next) //解决可答问题 { if(v[p1->v]) //由于我们一直都在标记父亲节点,此时如果与u相关的问题节点得到了遍历, { //那么就说明找到了u和它的一个问题节点的祖先节点,我们沿着问题节点回溯便是了 ans[p1->id]=dist[u]+dist[p1->v]-2*dist[find(p1->v)]; } } } //主函数 int main() { int t,i,a,b,w; char c; freopen("D:\\in.txt","r",stdin); while(scanf("%d%d",&n,&m)!=EOF) { memset(link,0,sizeof(link)); num=0; for(i=1;i<=m;i++) { scanf("%d %d %d %c",&a,&b,&w,&c); add(a,b,w); } memset(link1,0,sizeof(link1)); num1=0; scanf("%d",&t); for(i=1;i<=t;i++) //建立问题链表 { scanf("%d%d",&a,&b); add1(a,b,i); } memset(v,0,sizeof(v)); dist[1]=0; v[1]=1; tarjan(1,1); for(i=1;i<=t;i++) { printf("%d\n",ans[i]); } } return 0; }
相关文章推荐
- PKU 1986 Distance Queries LCA
- LCA tarjan 算法 练习: hdu 2586 + poj 1986
- LCA算法--并查集应用
- LCA之ST算法模板 poj-1986
- 【算法】【树】最近公共祖先LCA——Tarjan算法
- 顺序表应用3:元素位置互换之移位算法
- 进化算法应用 Evolutionary Principles applied to Problem Solving
- 【算法入门】Trie 树详解及其应用
- 【算法——Python实现】快速排序的优化:三路快速排序及Leetcode题目应用
- [Java算法分析与设计]单向链表(List)的实现和应用
- 二分算法的应用——Codevs 1766 装果子
- 数据结构,算法与应用(3)
- [转]Ultra Fractal教程系列06——快速入门教程03——应用着色算法
- 【数据结构与算法】数组应用3:稀疏矩阵压缩(Java实现)
- [算法设计与分析]3.4.4斐波那契数列的应用(上楼梯)
- Java实现URI参数签名算法,确保应用与REST服务器之间的安全通信,防止Secret Key盗用、数据篡改等恶意攻击行为
- Silverlight.XNA(C#)跨平台3D游戏研发手记:(二)四叉树遍历与人工智能A*算法在SLG移动路径范围测算中的应用
- otsu自适应阈值分割的算法描述和opencv实现,及其在肤色检测中的应用[转]
- 顺序表应用1:多余元素删除之移位算法
- Computer Vision: Algorithms and Applications 计算机视觉:算法与应用 翻译工作 序