您的位置:首页 > 其它

POJ 1986 Tarjan离线算法(最近公共祖先)

2016-08-26 19:09 330 查看
题意就是求两个点之间的最短距离 那个字母是没有用的
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N=40010;
vector<int> Q
,num
;//定义容器 Q[0]为一个容器 Q[1]为一个容器以此类推 每个容器都类似于一个数组 只是需要多大能开多大 节约内存(大概是这样我也不是很懂)
int fa
,d
,LCA
,qx
,qy
,n,m,g;
int Begin
,Next[N*2],to[N*2],w[N*2],e;
void Add(int x,int y,int k){//链式前向星(这个知道的应该很多吧)to[i]是第i条边的目标点 w[i]是第i条边的长度 next[i]指与第i条边的起始点相同的下一条边的编号 begin[j]指输入的最后一条以j为起始点的边的编号即从j点开始遍历的第一条边
to[++e]=y;
w[e]=k;
Next[e]=Begin[x];//至于最后两行为什么这样写 画个图就知道了很好理解
Begin[x]=e;
}//链接矩阵需要n*n的数组 链接表需要n*m的数组 前向星只需要几个m的一维数组 最节约内存
void Addq(int x, int y, int k){
Q[x].push_back(y);//压入
num[x].push_back(k);//Q[x]是以x为终点的询问的起点的集合 num[x]是以x为起点的询问的编号的集合
}
int find(int x){
return x==fa[x]?x:(fa[x]=find(fa[x]));
}
void Tarjan(int h, int dis){
fa[h]=h;//每个节点的父亲节点设为自己
d[h]=dis;//d[h]代表从h点到根节点即到1点的距离
for(int i=Begin[h];i;i=Next[i]){//链式前向星的应用 终止条件为编号变成0即后面没边了
int v=to[i];
if(!fa[v]){
Tarjan(v,dis+w[i]);
fa[v]=h;//回溯完再将这个点的父亲节点设置为父亲 自己模拟一下就知道为什么了
}
}
for(int i=0;i<=(int)Q[h].size()-1;i++){//因为h点正在遍历 所以只要询问中有一点为h点并且另一点已经遍历过就可以找他们的最近公共祖先了
int v = Q[h][i];
if(fa[v])
LCA[num[h][i]]=find(v);//LCA[i]指第i个询问(注意是询问)的两个点的最近公共祖先
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y,z;
scanf("%d%d%d%c%c",&x,&y,&z);
Add(x,y,z);
Add(y,x,z);
}
scanf("%d",&g);
for(int i=1;i<=g;i++){
scanf("%d%d",&qx[i],&qy[i]);
Addq(qx[i],qy[i],i);
Addq(qy[i],qx[i],i);
}
Tarjan(1,0);//从根节点开始遍历 1是根节点的编号 0是离根节点的距离 过程类似于dfs
for(int i=1;i<=g;i++)
printf("%d\n",d[qx[i]]+d[qy[i]]-2*d[LCA[i]]);;
return 0;
}


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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息