題目
藍橋杯歷屆習題中有一道題叫大臣的旅費,題目是這樣的:
乍一看這題可以使用DFS,對每個點都用一次DFS,求得最大的旅費(路徑權值)即可。但不幸的是,這麼做複雜度實在是太高了,壓力測試超時了。。。
於是就上網找各位網友的答案,發現這道題的考點是樹的直徑,用兩遍DFS即可得出答案。
樹的直徑
在一棵樹中,樹的直徑是兩個結點的最大距離(認爲樹中的邊權值非負),要求得這一距離,可以用兩遍DFS算法來求解。
第一遍DFS:從根節點開始遞歸深搜,找到距離根節點最遠的結點,記爲u。
第二遍DFS:從結點u開始遞歸深搜,找到距離u最遠的結點v。此時,d(u,v)記爲樹的直徑。
證明如下:
直觀:樹中最遠的兩個結點一定是葉子節點,不妨設爲u和v,其距離d(u,v)。
設有另外兩個葉子節點a,b與u,v不完全相同,且滿足d(a,b)>d(u,v),下面反證該假設不成立。設結點p是結點u,a,b三者的公共最近祖先,因此有
而且結點p一定在路徑(u,a)或路徑(u,b)上(否則p就不是最近的公共祖先了。不妨設結點p在路徑(u,a)上,因此
又因爲結點p是距離根節點最遠的點,因此
因爲結點v是距離u最遠的點,因此
結合(1)(2)(3)式得
即
因此,得出結論,u,v距離必是樹直徑。
解題
由上述結論,求解“大臣的旅費”問題,只需要兩遍DFS,第一遍求得距離城市1最遠的城市,記爲farest
,第二遍從farest
開始深搜,過程中就可以得到結果result
。
寫好代碼後,不幸的是,內存超了,發現是圖的表示用了二維數組,沒有考慮大規模數據問題。求改後,使用雙層嵌套Map
用映射來表示非負權值邊。順利AC。這裏改爲鄰接鏈表表示比較合適,但懶得改了。
還有一點需要注意的是本題中大臣的旅行開銷的計算方法很迷惑。。需要注意一下,找到規律後,等差數列求值。
package main;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
public class Main {
static int N = 0;
static Map<Integer, Map<Integer, Integer>> edges;
static int result = 0;
static int farest = 0;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
N = in.nextInt();
edges = new HashMap<Integer, Map<Integer, Integer>>();
for (int i = 1; i <= N; i++) {
edges.put(i, new HashMap<>());
}
for (int i = 0; i < N - 1; i++) {
int a = in.nextInt();
int b = in.nextInt();
int c = in.nextInt();
edges.get(a).put(b, c);
edges.get(b).put(a, c);
}
boolean[] vis = new boolean[N + 1];
vis[1] = true;
dfs(1, 1, vis, 0, 0);
result = 0;
vis = new boolean[N + 1];
vis[farest] = true;
dfs(1, farest, vis, 0, 0);
System.out.println(result);
in.close();
}
public static void dfs(int k, int city, boolean[] vis, int cost, int dis) {
// System.out.println("city: " + city + " k: " + k + " cost: " + cost);
for (Integer next : edges.get(city).keySet()) {
if (!vis[next]) {
vis[next] = true;
int pre_cost =
(21 + edges.get(city).get(next) + 2 * dis) * edges.get(city).get(next) / 2 + cost;
dfs(k + 1, next, vis, pre_cost, dis + edges.get(city).get(next));
vis[next] = false;
}
}
if (cost > result) {
result = cost;
farest = city;
}
}
}