一 问题描述
约翰有 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 的最短距离
}
八 测试