zoukankan      html  css  js  c++  java
  • How far away?

    C - How far away ?

     HDU - 2586 

    There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house B"? Usually it hard to answer. But luckily int this village the answer is always unique, since the roads are built in the way that there is a unique simple path("simple" means you can't visit a place twice) between every two houses. Yout task is to answer all these curious people.

    Input

    First line is a single integer T(T<=10), indicating the number of test cases. 
      For each test case,in the first line there are two numbers n(2<=n<=40000) and m (1<=m<=200),the number of houses and the number of queries. The following n-1 lines each consisting three numbers i,j,k, separated bu a single space, meaning that there is a road connecting house i and house j,with length k(0<k<=40000).The houses are labeled from 1 to n. 
      Next m lines each has distinct integers i and j, you areato answer the distance between house i and house j.

    Output

    For each test case,output m lines. Each line represents the answer of the query. Output a bland line after each test case.

    Sample Input

    2
    3 2
    1 2 10
    3 1 15
    1 2
    2 3
    
    2 2
    1 2 100
    1 2
    2 1

    Sample Output

    10
    25
    100
    100
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    
    const int N = 40010;
    const int M = 25;
    int dp[2 * N][M];
    bool vis[N];
    struct edge
    {
        int v, w, next;
    } e[2 * N];
    int tot, head[N];
    int tol;
    inline void add(int u,int v,int w)
    {
        e[tol].v = v;
        e[tol].w = w;
        e[tol].next = head[u];
        head[u] = tol++;
        u = u ^ v;
        v = v ^ u;
        u = v ^ u;
        e[tol].v = v;
        e[tol].w = w;
        e[tol].next = head[u];
        head[u] = tol++;
    }
    int ver[2 * N], R[2 * N], first[N], dir[N];
    void dfs(int u, int dep)
    {
        vis[u] = 1;
        ver[++tot] = u;
        first[u] = tot;
        R[tot] = dep;
        for (int k = head[u]; k != -1; k = e[k].next)
        {
            if (!vis[e[k].v]) {
                int v = e[k].v, w = e[k].w;
                dir[v] = dir[u] + w;
                dfs(v, dep + 1);
                ver[++tot] = u;
                R[tot] = dep;
            }
        }
    }
    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];
                int b = dp[i + (1 << (j - 1))][j-1];
                dp[i][j] = R[a] < R[b] ? a : b;
            }
        }
    }
    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 R[a] < R[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 ver[res];
    }
    int main()
    {
        int cas;
        scanf("%d", &cas);
        while (cas--)
        {
            int n, q;
            tol = 0;
            scanf("%d%d", &n, &q);
            memset(head, -1, sizeof(head));
            memset(vis, 0, sizeof(vis));
            for (int i = 1; i < n;i++) {
                int u, v, w;
                scanf("%d%d%d", &u, &v, &w);
                add(u, v, w);
            }
            tot = 0;
            dir[1] = 0;
            dfs(1, 1);
            ST(2 * n - 1);
            while (q--) {
                int u, v;
                scanf("%d%d", &u, &v);
                int lca = LCA(u, v);
                printf("%d
    ", dir[u] + dir[v] - 2 * dir[lca]);
            }
        }
        getchar();
        getchar();
        return 0;
    }

    这是一道最近公共祖先的问题。

    其实主要还是对RMQ-ST算法的理解,我的那篇  最近公共祖先-三  博客写的不是很清楚,这里就写明白一点。

    首先说一下链式向前星,就是代码里面的双向建图。head数组里面存的是每一个起点的最大边号,然后next数组里面存的是该起点之前边的边号。

    也就是说,假设点 i ,连接着 1 3 5 边号的边,head数组里面存的就是 5 ,然后next数组里面存的就是边5的前一条边,比如4号边,4和5是一条直接联通的边,然后还顺次连结着其它的边。

    这样的话,在dfs搜索的时候就可以通过for循环,将某条联通的边给遍历完。因为 i 是在不断被赋值给next[ i ]的,然后next[ i ]里面存的就是前一条边的边号,以此类推就可以让for循环遍历完整个路径。

    对于向下搜索时,起到引导作用的是,该点指向的点,然后跳转到目标点,进行目标点的深搜,dfs是一个横向加纵向的过程。

    然后就是dfs的回溯了,说的很高大上,其实不然,回溯也就是在走一遍之前走过的点。因为当目前的路走不通时,也就是for循环执行完毕,内层的dfs执行也就结束了,然后走到外层,该执行dfs的下一句话了,执行的这句话就是回溯。

    接着说RMQ-ST,它实际上是通过深搜得到遍历时走过点的顺序加编号实现的。

    设每一个点都有它独立的编号,然后第几步走过该点成为序号。

    dfs深搜的时候要建立一个序号数组,简称X,建立一个深度数组,简称S,建立一个第一次访问数组,简称F。

    它们里面存的是,序号数组:序号作为下标,编号作为内容。

    深度数组:序号作为下标,深度作为内容。

    第一次访问数组:编号作为下标,第一次出现的序号作为内容。

    然后这就简单了,当输入两个点的时候,通过F数组找到它们的第一次序号,然后两个数从小到大排列,分别作为左端点和右端点。利用已经存下的dp数组,查找对应区间的最小深度对应的序号,用S数组比较两个序号的大小,返回深度较小的序号。再通过序号,利用F数组查找是哪个点第一次出现在该序号上,这就找到了。

    当然,这说的只是大致过程,现在说所dp数组。dp数组是是怎么形成的呢?

    大致是这样的,区间长度为一的数组dp[ i ][ 0 ]应存入该序号,易证。

    我们要得到的是该区间深度最小值对应的序号,然后再比较的dp[ i ][ 1 ] ,2的一次方是2,然后区间长度就是2,然后就把之前的区间长度为一的dp[ i ][ 0 ]拿来比较,比较该序号的深度最小值,因为有S数组,有序号对应深度的关系。

    以上就是全过程了,细节的实现,放几个传送门吧。

    链式向前星:https://blog.csdn.net/qq_40046426/article/details/81906436

    RMQ-ST带图:https://blog.csdn.net/gesanghuazgy/article/details/51498213

    ST算法:https://blog.csdn.net/qq_41090676/article/details/82713912

  • 相关阅读:
    使用FolderBrowserDialog组件选择文件夹
    使用OpenFileDialog组件打开多个文
    使用OpenFileDialog组件打开对话框
    获取弹出对话框的相关返回值
    PAT 甲级 1139 First Contact (30 分)
    PAT 甲级 1139 First Contact (30 分)
    PAT 甲级 1138 Postorder Traversal (25 分)
    PAT 甲级 1138 Postorder Traversal (25 分)
    PAT 甲级 1137 Final Grading (25 分)
    PAT 甲级 1137 Final Grading (25 分)
  • 原文地址:https://www.cnblogs.com/xyqxyq/p/10211381.html
Copyright © 2011-2022 走看看