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
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.
5 3 2 1 3 2 2 4 3 5 2 3 1 4 4 5
Not connected 6HintHint Huge input, scanf recommended.
最近做了好几道类似的题目,把自己理解的详细过程写一下,加深印象。
题意:一些城市组成一些树(可能不止一颗),给出城市之前的连接情况,求两个城市之间最短距离。
思路:大致思路就是,首先,如果两个城市在同一棵树上,分别为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");
}
}
}