首页 > 代码库 > hdu2586How far away ?
hdu2586How far away ?
就是说给你n个点,n-1条边构成一个无向图。
然后还有m次询问,每次问你两个点的最短距离。
于是转换成最短路问题?
嗯,最多有4万个点,边的话算是4万条吧,
于是用spfa的话,每次算一个单源点的最短路,
大概是32*10的8次方时间复杂度,
题目只给了1秒,
所以超时的节奏。
有一个叫做tarjan的算法,
是用来求两个节点的最近公共祖先的,
听说是正常人能写出的求此类方法的最快算法,
反正我是没看懂。
于是这道题可以转换成求最近公共祖先,
求的过程顺便算下每个点到根节点的距离就可以
求这道题了。
tarjan算法的时间渐进复杂度是询问数+图中边的数目,
于是就是8万的样子,不会超时的样子。
那么这个算法怎么写,我觉得是这样的。
它大概是用dfs和并查集实现的,
为了加快访问边的速度,我还用到了链式向前星
这个数据结构。
具体点大概是这样,
从根节点开始搜索,
每次搜索一开始就把当前搜索的那个点
标记为已经加入并查集,
加入并查集就意味着,
这个点已经加入了某棵子树,
这就意味着已经跟某个节点有了最近公共祖先,
至于某点是不是要查询的那个点就不知道了。
怎么求出要查询的两点的最近公共祖先呢?
枚举所有查询,
如果查询两个点中任意一点正好是正在搜索的
那个点,并且另一个点已经加入了某棵子树,
则把这两个点的最近公共祖先设置为此点
与某点的最近公共祖先,
至于为毛这样,不造。
然后搜索所有以这个当前搜索点的为前驱的
没有加入任何子树的后继节点,
直到所有的节点都加入了某棵子树,
这个过程就结束。
好吧,我的代码如下:
#include<iostream> #include<cstring> using namespace std; int num_dot,num_q,num_side,cnt,box[40010],dis[40010],father[40010],vis[40010]; struct node { int e,next,w; }side[80010]; struct node1 { int s,e,f; }q[210]; void add(int s,int e,int w) { side[cnt].e=e; side[cnt].w=w; side[cnt].next=box[s]; box[s]=cnt++; } void init() { int t1,t2,t3; cnt=0; scanf("%d%d",&num_dot,&num_q); num_side=num_dot-1; memset(box,-1,sizeof(box)); memset(vis,0,sizeof(vis)); vis[1]=1; dis[1]=0; for(int i=0;i<num_side;i++) { scanf("%d%d%d",&t1,&t2,&t3); add(t1,t2,t3); add(t2,t1,t3); } for(int i=0;i<num_q;i++) scanf("%d%d",&q[i].s,&q[i].e); } int find(int x) { while(x!=father[x]) x=father[x]; return x; } void dfs(int mid) { vis[mid]=1; father[mid]=mid; for(int i=0;i<num_q;i++) { if(q[i].s==mid&&vis[q[i].e]) q[i].f=find(q[i].e); else if(q[i].e==mid&&vis[q[i].s]) q[i].f=find(q[i].s); } for(int i=box[mid];i!=-1;i=side[i].next) if(!vis[side[i].e]) { dis[side[i].e]=dis[mid]+side[i].w; dfs(side[i].e); father[side[i].e]=mid; } } int main() { int exp; scanf("%d",&exp); while(exp--) { init(); dfs(1); for(int i=0;i<num_q;i++) printf("%d\n",dis[q[i].s]+dis[q[i].e]-2*dis[q[i].f]); } }
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。