您的位置:首页 > 理论基础 > 数据结构算法

HDU2874——Connections between cities 详解 (LCA,RMQ,数据结构,dfs序,并查集)

2016-07-26 11:53 417 查看


Connections between cities

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 9013    Accepted Submission(s): 2178


Problem Description

After World War X, a lot of cities have been seriously damaged, and we need to rebuild those cities. However, some materials needed can only be produced in certain places. So we need to transport these materials from city to city. For most of roads had been
totally destroyed during the war, there might be no path between two cities, no circle exists as well.

Now, your task comes. After giving you the condition of the roads, we want to know if there exists a path between any two cities. If the answer is yes, output the shortest path between them.

 

Input

Input consists of multiple problem instances.For each instance, first line contains three integers n, m and c, 2<=n<=10000, 0<=m<10000, 1<=c<=1000000. n represents the number of cities numbered from 1 to n. Following m lines, each line has three integers i,
j and k, represent a road between city i and city j, with length k. Last c lines, two integers i, j each line, indicates a query of city i and city j.

 

Output

For each problem instance, one line for each query. If no path between two cities, output “Not connected”, otherwise output the length of the shortest path between them.

 

Sample Input

5 3 2
1 3 2
2 4 3
5 2 3
1 4
4 5

 

Sample Output

Not connected
6

Hint
Hint

Huge input, scanf recommended.

 

Source

2009 Multi-University Training Contest 8 -
Host by BJNU

 

最近做了好几道类似的题目,把自己理解的详细过程写一下,加深印象。

题意:一些城市组成一些树(可能不止一颗),给出城市之前的连接情况,求两个城市之间最短距离。

思路:大致思路就是,首先,如果两个城市在同一棵树上,分别为u和v,那么他们的最短距离就是u到lca(最近公共祖先)和v到lca的距离之和。也就相当于是,u到根节点的距离和v到根节点的距离之和减去两倍根节点到LCA的距离。

那么问题来了。

①怎么求一个点到根节点的距离以及求LCA?

显然,我们在遍历一棵树的时候,在两个节点间的最短路径上,深度最小的那个节点就是lca。所以我们用dfs遍历,在每次遍历到这个点的时候打上时间戳,回溯到的时候再打一个时间戳(注意节点的遍历次数可能大于2,为了保证两个节点间包含了全部路径),在dfs遍历的时候就可以处理深度和到根节点的距离问题。

这样我们就能把一棵树变成一个链式结构。每两个节点第一次出现的地方中间就是他们的最短路径。其中深度最小的就是LCA。查询的时候可以用ST表或者树状数组。一个是O(1)的,一个是O(lgn)的,都能满足要求。

②怎么判断两个节点时候在同一棵树上?

把相连的节点用并查集合并,在计算前查询。

#include <cmath>
#include <cstring>
#include <cstdio>
#include <vector>
#include <string>
#include <algorithm>
#include <string>
#include <map>
#include <queue>
#include <set>
#include <stack>
using namespace std;
#define MAXN 40010
#define LEN 200010
#define INF 1e9+7
#define MODE 1000000
#define pi acos(-1)
#define g 9.8
typedef long long ll;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
struct edge{
int next,to,w;
};

edge G[MAXN*2];
int root;
int num=0;
int cnt=0;
int head[MAXN<<1];
int depth[MAXN<<1];//深度
int first[MAXN<<1];//首次出现编号
int dir[MAXN<<1];//距离
int que[MAXN<<1];//队列
int par[MAXN];//并查集父节点
bool vis[MAXN];
int dp[MAXN][20];
void init(int n){for(int i=0;i<=n;i++)par[i]=i;}//初始化并查集
int _find(int x){if(par[x]==x)return x;else return par[x]=_find(par[x]);}//查询并查集祖先
void unite(int x,int y){x=_find(x),y=_find(y);if(x==y)return;else par[x]=y;}//合并节点
void add(int u,int v,int w){G[num].w=w;G[num].to=v;G[num].next=head[u];head[u]=num++;}//前向星建图
void dfs(int u,int dep)//dfs建图
{
vis[u]=true;
que[++cnt]=u;first[u]=cnt;depth[cnt]=dep;
for(int k=head[u];k!=-1;k=G[k].next)
{
int v=G[k].to,w=G[k].w;
if(!vis[v]){
dir[v]=dir[u]+w;
dfs(v,dep+1);
que[++cnt]=u;depth[cnt]=dep;
}
}
}
//ST表求LCA
void ST(int n)
{
for(int i=1;i<=n;i++)
dp[i][0] = i;
for(int j=1;(1<<j)<=n;j++)
{
for(int i=1;i+(1<<j)-1<=n;i++)
{
int a = dp[i][j-1] , b = dp[i+(1<<(j-1))][j-1];
dp[i][j] = depth[a]<depth[b]?a:b;
}
}
}
//两个节点之间的路径深度最小的就是LCA
int RMQ(int l,int r)
{
int k=0;
while((1<<(k+1))<=r-l+1)
k++;
int a = dp[l][k], b = dp[r-(1<<k)+1][k]; //保存的是编号
return depth[a]<depth[b]?a:b;
}
int LCA(int u ,int v)
{
int x = first[u] , y = first[v];
if(x > y) swap(x,y);
int res = RMQ(x,y);
return que[res];
}
int n,m,c;
int main()
{
while(scanf("%d%d%d",&n,&m,&c)!=EOF){
num=0,cnt=0;
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
init(n);
for(int i=0;i<m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
unite(u,v);
}
for(int i=1;i<=n;i++)
{
if(_find(i)==i){
dir[i]=0;
dfs(i,1);
}
}
ST(2*n-1);
while(c--)
{
int u,v;
scanf("%d%d",&u,&v);
if(_find(u)==_find(v))
{
int lca=LCA(u,v);
printf("%d\n",dir[u]+dir[v]-2*dir[lca]);
}
else
printf("Not connected\n");
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: