zoukankan      html  css  js  c++  java
  • HDU2586 How far away ? LCATarjan Or spfa

    题意:给定N个节点一棵树,现在要求询问任意两点之间的简单路径的距离,其实也就是最短路径距离。

    解法:spfa:直接对每一对点作一次spfa即可。Tarjan:求出两个点A,B之间的最近公共祖先C,设根到每个点的最短距离为dis[],那么距离就是dis[A]+dis[B]-2*dis[C]。而根到各个点的距离一次dfs就出来了,因此问题转化为求出两点之间的最近公共祖先,Tarjan算法能够离线解决所询问的点对。原理如下:

    对于一次dfs,当第一次遍历到点u时,那么令set[u] = u。遍历完以u为根的树后,将u所属集合指向双亲节点。接下来查看u的访问列表中有没有询问<u, v>的点对,如果有且v已经被访问过,则<u, v>的最近公共祖先为find(v)。如果u是v下面的节点,那么根据这种遍历方式,显然find(v) = v,否则find(v)指向的最近的一个公共祖先,因为u的进入一定是经过这个最近的公共祖先进入的。

    代码如下:Spfa:62MS   Tarjan 15MS

    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    using namespace std;
    
    int N, Q;
    
    struct Edge {
        int v, fee, next;    
    };
    Edge e[80005];
    
    struct Node {
        int v, num, next;
    }q[405];
    int idx, head[40005];
    int cnt, link[40005];
    int set[40005];
    int dis[40005];
    int rec[205][3];
    char vis[40005];
    
    void insert(int a, int b, int fee) {
        e[idx].v = b, e[idx].fee =fee;
        e[idx].next = head[a];
        head[a] = idx++;
    }
    
    void push(int a, int b, int num) {
        q[cnt].v = b, q[cnt].next = link[a];
        q[cnt].num = num, link[a] = cnt++;
    }
    
    int find(int x) {
        return set[x] = x == set[x] ? x : find(set[x]);
    }
    
    void tarjan(int u) {
        vis[u] = 1; // 标记该点已经走过 
        set[u] = u;
        // 在第一次进入该点的时候就将该点标记为暂时的根节点,为孩子节点指向它做准备
        for (int i = link[u]; ~i; i = q[i].next) { // 查看该节点后面时候有询问存在 
            int v = q[i].v;
            if (vis[v]) {
                rec[q[i].num][2] = find(v);
            }
        }
        for (int i = head[u]; ~i; i = e[i].next) {
            int v = e[i].v;
            if (!vis[v]) {
                dis[v] = dis[u] + e[i].fee;
                tarjan(v);
                set[v] = u; // 此时放弃孩子节点为根,调整孩子节点的根指向更上层的节点    
            }
        }
    }
    
    int main() {
        int T, a, b, fee;
        for (scanf("%d", &T); T; T--) {
            scanf("%d %d", &N, &Q);
            cnt = idx = 0;
            memset(head, 0xff, sizeof (head));
            memset(link, 0xff, sizeof (link));
            for (int i = 1; i < N; ++i) {
                scanf("%d %d %d", &a, &b, &fee);
                insert(a, b, fee), insert(b, a, fee);
            }
            for (int i = 0; i < Q; ++i) {
                scanf("%d %d", &a, &b);
                push(a, b, i), push(b, a, i); // Tarjan是一个离线算法,因此需要先保存询问
                rec[i][0] = a, rec[i][1] = b; // 记录起询问 
            }
            memset(vis, 0, sizeof (vis)); // 标记点
            dis[1] = 0;
            tarjan(1);
            for (int i = 0; i < Q; ++i) {
                printf("%d\n", dis[rec[i][0]]+dis[rec[i][1]]-2*dis[rec[i][2]]);    
            }
        }
        return 0;    
    }
    Tarjan
    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    int N, Q;
    
    struct Edge {
        int v, fee, next;    
    }e[80005];
    int idx, head[40005];
    int dis[40005];
    
    void insert(int a, int b, int fee) {
        e[idx].v = b, e[idx].fee = fee;
        e[idx].next = head[a];
        head[a] = idx++;
    }
    
    int que[400005];
    int front, tail;
    char vis[40005];
    
    int spfa(int s, int t) {
        memset(dis, 0x3f, sizeof (dis));
        memset(vis, 0, sizeof (vis));
        front = tail = 0;
        que[tail++] = s;
        dis[s] = 0;
        while (front < tail) {
            int v, u = que[front++];
            vis[u] = 0;
            for (int i = head[u]; ~i; i = e[i].next) {
                v = e[i].v;
                if (dis[v] > dis[u] + e[i].fee) {
                    dis[v] = dis[u] + e[i].fee;
                    if (!vis[v]) {
                        vis[v] = 1;
                        que[tail++] = v;
                    }
                }
            }
        }
        return dis[t];
    }
    
    int main() {
        int T, a, b, fee;
        scanf("%d", &T);
        while (T--) {
            scanf("%d %d", &N, &Q);
            idx = 0;
            memset(head, 0xff, sizeof (head));
            for (int i = 1; i < N; ++i) {
                scanf("%d %d %d", &a, &b, &fee);
                insert(a, b, fee), insert(b, a, fee);
            }
            for (int i = 0; i < Q; ++i) {
                scanf("%d %d", &a, &b);
                printf("%d\n", spfa(a, b));
            }
        }
        return 0;    
    }
    Spfa
  • 相关阅读:
    NYOJ:喷水装置(一)
    NYOJ:QQ农场
    NYOJ:死神来了(鸽巢定理)
    NYOJ:星际之门(一)(cayley定理)
    计蒜客: 法师康的工人 (贪心)
    '-[UIViewController _loadViewFromNibNamed:bundle:] loaded the "AttentionController" nib but the view outlet was not set.'
    UITabBar 蓝色
    App installation failed There was an internal API error.
    UIImage将图片写入本地相册
    UINavigationItem不显示
  • 原文地址:https://www.cnblogs.com/Lyush/p/3083776.html
Copyright © 2011-2022 走看看