HDU-2586 How far away ? LCA-Tarjan 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

 

posted @ 2013-05-17 15:34  沐阳  阅读(335)  评论(0编辑  收藏  举报