距离查询问题

一 问题描述

约翰有 N 个农场,标记为 1 到 N,有 N 条垂直和水平道路连接。农场每条道路长度各不相同,每个农场都可以直接连接到北部,南部,东部或西部最多 4 个其他农场。农场位于道路的终点,正好一条道路连接一对农场,没有两条道路交叉。他希望知道两个农场之间的道路长度,“1 6 13 E” 表示,从 F1 到 F6 有一条长度为 13 的道路,F6 在 F1的东部。

二 输入

第 1 行包含两个整数 N 和 M,第 2 到 M+1 行,每行都包含四个字符。a、b、l、d,表示两个农场 a 和 b 由一条路相连,长度为 l,d 是字符 N S E 或 W,表示从 a 到 b 的道路方向。第 M+2 行包含单个整数 K,表示查询个数。接下来的 K 行,每行都包含距离查询的两个农场的编号。

三 输出

对每个查询都单行输出两个农场的距离。

四 输入样例

7 6

1 6 13 E

6 3 9 E

3 5 7 S

4 1 3 N

2 4 20 W

4 7 2 S

3

1 6

1 4

2 6

五 输出样例

13

3

36

六 算法分析和设计

1 分析

本问题实际上为树上距离查询问题,可以采用 Tarjan 算法离线处理所有查询。

2 设计

a 根据输入数据采用链式前向星存储图。

b 采用 Tarjan 算法离线处理所有查询。

七 代码

package com.platform.modules.alg.alglib.poj1986;

public class Poj1986 {
    public String output = "";
    private final int maxn = 80010;
    private int fa[];
    private int head[];
    private int qhead[];
    private int dis[];
    private boolean vis[];
    private int id;
    private int iq;

    Node E[] = new Node[maxn];
    Node QE[] = new Node[maxn];

    int find(int x) {
        if (x != fa[x])
            fa[x] = find(fa[x]);
        return fa[x];
    }

    void LCA(int u) {
        fa[u] = u;
        vis[u] = true;
        for (int i = head[u]; i != -1; i = E[i].next) {
            int v = E[i].to;
            if (!vis[v]) {
                dis[v] = dis[u] + E[i].lca;
                LCA(v);
                fa[v] = u;
            }
        }
        for (int i = qhead[u]; i != -1; i = QE[i].next) {
            int v = QE[i].to;
            if (vis[v]) {
                QE[i].lca = dis[u] + dis[v] - 2 * dis[find(v)];
                QE[i ^ 1].lca = QE[i].lca;
            }
        }
    }

    void add1(int u, int v, int w) {
        E[id].to = v;
        E[id].lca = w;
        E[id].next = head[u];
        head[u] = id++;
    }

    void add2(int u, int v) {
        QE[iq].to = v;
        QE[iq].next = qhead[u];
        qhead[u] = iq++;
    }

    void init() {
        fa = new int[maxn];
        head = new int[maxn];
        for (int i = 0; i < head.length; i++) {
            head[i] = -1;
        }
        qhead = new int[maxn];
        for (int i = 0; i < qhead.length; i++) {
            qhead[i] = -1;
        }
        vis = new boolean[maxn];

        for (int i = 0; i < E.length; i++) {
            E[i] = new Node();
        }
        for (int i = 0; i < QE.length; i++) {
            QE[i] = new Node();
        }

        dis = new int[maxn];
        id = iq = 0;
    }

    public String cal(String input) {
        int N, M, K, u, v, w;
        char s;
        String[] line = input.split("\n");
        String[] word = line[0].split(" ");
        N = Integer.parseInt(word[0]);
        M = Integer.parseInt(word[1]);
        init();
        for (int i = 0; i < M; i++) {
            String[] info = line[i + 1].split(" ");
            u = Integer.parseInt(info[0]);
            v = Integer.parseInt(info[1]);
            w = Integer.parseInt(info[2]);
            s = info[3].charAt(0);
            add1(u, v, w);
            add1(v, u, w);
        }
        K = Integer.parseInt(line[M + 1]);

        for (int i = 0; i < K; i++) {
            String[] query = line[M + 2 + i].split(" ");
            u = Integer.parseInt(query[0]);
            v = Integer.parseInt(query[1]);
            add2(u, v);//查询也用链式前向星存储
            add2(v, u);
        }
        LCA(1);
        for (int i = 0; i < iq; i += 2)
            output += QE[i].lca + "\n";
        return output;
    }
}

// 图中的边 E 和查询 QE 用统一结构体
class Node {
    public int to;
    public int next;
    public int lca; // lca 表示u、v的边权,查询中 lca 代表查询 u、v 的最短距离
}

八 测试

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值