POJ 1986 Distance Queries Tarjan算法求最近公共祖先+前向星
2017-08-18 09:01
375 查看
第一次尝试自己写博客,如果有什么不好的地方还请大家多多批评指正~
传送门:POJ 1986
题目大意:John是一个农场主,他的牛都懒散惯了,所以拒绝按照John所选的路走。John就想找一条最短的路。本题输入前半部分与“导航噩梦”相同,在每组数据之后会有一个整数K,接下来K行每行一个“距离询问”。每个距离询问包含两个整数,表示John想要计算距离的两个农场的编号。
Sample Input
Sample Output
思路:因为是求任意两个农场之间的距离,所以第一想法是用Floyd算法求多源最短路。但是该算法时间复杂度为O(n^3),有1w组询问,会超时。经过分析我们发现题目给出的方向完全没影响,可以忽略。并且两个节点之间至多有1条路,根据数据建图一定是一颗树。所以我们可以先把任一节点作为根节点,用深搜获取每个节点到根节点的距离,然后用Tarjan算法求最近公共祖先以及两点间的距离。
具体实现时深搜求距离和Tarjan算法求最近公共祖先可以同时进行,由于储存点的时候既要方便遍历与某节点相邻的所有节点,又要存储边权,同时最好能直接通过下标来访问数据。一般的结构体或者vector数组就不好用了。这里采用了“前向星”的数据结构。ACdreamers大牛讲解前向星
点击此处可以查看前向星的知识,非常好用的一种数据结构,建议掌握。至于Tarjan算法的实现,利用了并查集和前向星的知识,不懂的自行百度吧~
递归求节点i到根节点的距离时,公式为:节点i对应起点到根节点的距离 + 起点到i的距离。对于所要求的两点间的距离公式为:两点到根节点的距离和 - 2 * 两点的最近公共祖先到根节点的距离,不懂的画个图就可以明白了。
具体实现还是看代码吧,里面给了比较详细的注释,若还看不懂欢迎提问。
传送门:POJ 1986
题目大意:John是一个农场主,他的牛都懒散惯了,所以拒绝按照John所选的路走。John就想找一条最短的路。本题输入前半部分与“导航噩梦”相同,在每组数据之后会有一个整数K,接下来K行每行一个“距离询问”。每个距离询问包含两个整数,表示John想要计算距离的两个农场的编号。
Sample Input
7 6 1 6 13 E 6 3 9 E 3 5 7 S 4 1 3 N 2 4 20 W 4 7 2 S 3 1 6 1 4 2 6
Sample Output
13 3 36
思路:因为是求任意两个农场之间的距离,所以第一想法是用Floyd算法求多源最短路。但是该算法时间复杂度为O(n^3),有1w组询问,会超时。经过分析我们发现题目给出的方向完全没影响,可以忽略。并且两个节点之间至多有1条路,根据数据建图一定是一颗树。所以我们可以先把任一节点作为根节点,用深搜获取每个节点到根节点的距离,然后用Tarjan算法求最近公共祖先以及两点间的距离。
具体实现时深搜求距离和Tarjan算法求最近公共祖先可以同时进行,由于储存点的时候既要方便遍历与某节点相邻的所有节点,又要存储边权,同时最好能直接通过下标来访问数据。一般的结构体或者vector数组就不好用了。这里采用了“前向星”的数据结构。ACdreamers大牛讲解前向星
点击此处可以查看前向星的知识,非常好用的一种数据结构,建议掌握。至于Tarjan算法的实现,利用了并查集和前向星的知识,不懂的自行百度吧~
递归求节点i到根节点的距离时,公式为:节点i对应起点到根节点的距离 + 起点到i的距离。对于所要求的两点间的距离公式为:两点到根节点的距离和 - 2 * 两点的最近公共祖先到根节点的距离,不懂的画个图就可以明白了。
具体实现还是看代码吧,里面给了比较详细的注释,若还看不懂欢迎提问。
#include<stdio.h> #include<string.h> #define MAX 80005 int id,iq; //分别记录存储的点和询问的个数 int f[MAX],vis[MAX],dis[MAX]; //dis[i]记录根节点到i的距离 //head[i]记录以i为起点的第一条边的下标,qhead类似 int head[MAX],qhead[MAX]; struct node { //前向星 int w; //两点间权值 int to; //终点 int next; //和to起点相同的下一条边的存储下标 } edge[MAX],que[MAX]; void add_edge(int u,int v,int w) { //加点 edge[id].to=v; edge[id].w=w; edge[id].next=head[u]; head[u]=id++; edge[id].to=u; edge[id].w=w; edge[id].next=head[v]; head[v]=id++; } void add_que(int u,int v) { //加询问 que[iq].to=v; que[iq].next=qhead[u]; qhead[u]=iq++; que[iq].to=u; que[iq].next=qhead[v]; qhead[v]=iq++; } void init(int n) { //并查集的初始化函数 int i; for(i=0;i<=n;i++) f[i]=i; memset(vis,0,sizeof(vis)); memset(dis,0,sizeof(dis)); memset(head,-1,sizeof(head)); //head初始化为-1,表示无下一条边 memset(qhead,-1,sizeof(qhead)); } int find(int x) { //并查集的压缩路径版查找函数 if(x!=f[x]) f[x]=find(f[x]); return f[x]; } void Tarjan(int root) { int i; vis[root]=1; f[root]=root; for(i=head[root];i!=-1;i=edge[i].next) { //不断搜索以当前顶点为起点的节点 if(!vis[edge[i].to]) { //根节点到终点的距离=根节点到起点的距离 + 边权 dis[edge[i].to]=dis[root]+edge[i].w; Tarjan(edge[i].to); f[edge[i].to]=root; } } for(i=qhead[root];i!=-1;i=que[i].next) { //查询和当前节点有关的询问 if(vis[que[i].to]) { //两点间距离为两点到根节点的距离和 - 两倍的最近总共祖先到根节点距离 que[i].w=dis[root]+dis[que[i].to]-2*dis[find(que[i].to)]; que[i^1].w=que[i].w; //第i和i+1的结果相同(i为偶数) } } } int main() { int i,t,n,m,k,u,v,w; scanf("%d%d",&n,&m); //顶点数和边数 init(n); id=0; for(i=0;i<m;i++) { scanf("%d%d%d%*c%*c",&u,&v,&w); //两节点及其之间的权值 add_edge(u,v,w); } scanf("%d",&k); //询问数 iq=0; for(i=0;i<k;i++) { scanf("%d%d",&u,&v); add_que(u,v); } Tarjan(1); //选节点1作为根节点 for(i=0;i<iq;i+=2) printf("%d\n",que[i].w); return 0; }
相关文章推荐
- 【POJ 1986】Distance Queries 最近公共祖先
- POJ1986 DistanceQueries 最近公共祖先LCA 离线算法Tarjan
- POJ1986 DistanceQueries 最近公共祖先LCA 离线算法Tarjan
- ACM: 最近公共祖先tarjan poj 1986
- poj 1986 最近公共祖先 (lca 倍增)
- poj 1986 Distance Queries(最近公共祖先的tarjan算法)
- POJ 1986 Distance Queries / UESTC 256 Distance Queries / CJOJ 1129 【USACO】距离咨询(最近公共祖先)
- 用于求最近公共祖先(LCA)的 Tarjan算法–以POJ1986为例(转)
- POJ 1986 Distance Queries (最近公共祖先,tarjan)
- POJ 1986 Distance Queries(求最近公共祖先,LCA-Tarjan)
- POJ 1986 Tarjan离线算法(最近公共祖先)
- poj 1986(离线查询点对最近公共祖先)
- POJ 1330 Nearest Common Ancestors(最近公共祖先)
- 树的最近公共祖先——LCA poj1330 hdu2586
- POJ 1330 最近公共祖先LCA(Tarjan离线做法)
- [Poj]1330——最近公共祖先
- POJ 1470 Closest Common Ancestors(最近公共祖先)
- POJ 1330 Nearest Common Ancestors 最近公共祖先 欧拉序列RMQ
- POJ1470翻译 最近公共祖先
- Nearest Common Ancestors+POJ+一道最近公共祖先的模板题