[Poi0202]Travelling Salesman 最近公共祖先

本文介绍了一种计算商人从首都出发经过多个城镇的最短旅行时间的方法。利用DFS深度优先搜索算法建立城镇间的连接关系,并通过LCA最近公共祖先算法求解路径长度。

Description

某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间。
假设有N个城镇,首都编号为1,商人从首都出发,其他各城镇之间都有道路连接,任意两个城镇之间如果有直连道路
在他们之间行驶需要花费单位时间。该国公路网络发达,从首都出发能到达任意一个城镇,并且公路网络不会存在环。
你的任务是帮助该商人计算一下他的最短旅行时间。

Input

第一行有一个整数N,1<=n<=30 000,为城镇的数目。下面N-1行,每行由两个整数a 和b
(1<=a, b<=n; a<>b)组成,表示城镇a和城镇b有公路连接。在第N+1行为一个整数M,下面的M行,
每行有该商人需要顺次经过的各城镇编号。

Output

输出该商人旅行的最短时间。

Sample Input

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

Sample Output

7

题解:模板,几乎一样的代码,练手。

直接附上代码:
#include<cstdio>
#include<algorithm>
#define MAXN 30001
using namespace std;
struct Edge{int v,next,z;} edge[MAXN*2];
int cnt,deep[MAXN],fa[MAXN][25],n,m,head[MAXN],ans;
void add(int u,int v)
{
    edge[++cnt].v=v;
    edge[cnt].next=head[u];
    head[u]=cnt;
}
void dfs(int now,int father,int dep)
{
    fa[now][0]=father;
    deep[now]=dep;
    for(int i=head[now];i;i=edge[i].next)
        if(father!=edge[i].v)dfs(edge[i].v,now,dep+1);
}
void getf()
{
    for(int j=1;j<=20;j++)
        for(int i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
}
int gets(int a,int b)
{
    for(int i=0;i<=20;i++)
        if(b&(1<<i))a=fa[a][i];
    return a;
}
int lca(int u,int v)
{
    if(deep[u]<deep[v])swap(u,v);
    u=gets(u,deep[u]-deep[v]);
    if(u==v)return u;
    for(int i=20;i>=0;i--)
        if(fa[u][i]!=fa[v][i])
            u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}
int main()
{
    scanf("%d",&n);
    for(int i=1,x,y;i<n;i++)
        scanf("%d%d",&x,&y),add(x,y),add(y,x);
    dfs(1,1,0);
    getf();
    int u,v;
    scanf("%d%d",&m,&u);
    for(int i=1;i<m;i++)
    {
        scanf("%d",&v);
        int l=lca(u,v);
        ans+=deep[u]+deep[v]-deep[l]*2;
        u=v;
    }
    printf("%d",ans);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值