2586. How far away ?(HDU)
題意理解
一個村莊有很多戶人家,任意兩戶人家最多有一條路相連。已知每條路相連的兩戶人家的編號和長度,求從A戶人家到B戶人家有多遠?
問題分析
LCA+RMQ資料結構,最近公共祖先+區間最小值
轉1:首先用圖表示村莊的連線情況。使用鄰接表儲存,使用heads陣列存節點資訊,使用edges陣列存邊資訊,edges陣列的每個元素存邊的長度和鄰接邊的指標(這裡用陣列表示連結串列,所以指標其實是邊的下標)。
轉2:求A戶人家到B戶人家的最近距離,轉化為求A戶和B戶人家都相連的最近的那個節點C,再基於這個節點C,計算從根到A的距離disA,從根到B的距離disB,從根到C的距離disC,所求距離即為disA+disB-disC*2;
轉3:求A戶和B戶都相連的最近的節點,即是求最近公共祖先的問題,求最近公共祖先的問題轉化為求區間最小值問題。
轉4:深度優先遍歷圖,記錄下每個節點第一次出現的次序pos,記錄下每個節點的尤拉巡遊的次序t(即遍歷的全路徑,節點重複出現),記錄下每個節點相對根節點的深度dep。
轉4a:對於圖上任意兩個節點A,B,通過pos陣列找到他們出現在尤拉巡遊次序t上的位置,這是一個區間(t[A],t[B]),在將這個區間對映到深度dep對應的區間(dep[t[A]], dep[t[B]), 求深度dep這個區間的最小值,即最小深度值,這個深度值對應的位置就是最小公共祖先的編號。
轉4b:求深度dep次序中區間的最小值,使用區間最小值演算法,此演算法的要點是變換區間輸入的兩個端點位置為一個端點+區間長度。區間長度用2的冪表示。變換的過程是先計算區間長度,然後計算左端點+比區間長度小的最近的2的冪的最小值left、右端點-區間長度小的最近的2的冪對應的新的左端點+比區間長度小的最近的2的冪的最小值right,求left和right的最小值即為左右端點間的最小值。
轉4c:求左端點+用2的冪表示的區間長度範圍的最小值。需要計算整個dep序列全部可能的情況。容易想到的思路是左端點依次遍歷序列,長度計算整個序列的最近的2的冪的值,從0開始遍歷。這裡可以用動規來處理,思路是,先計算長度為1的值,再計算長度為2的值,再計算長度為2^2的值,...依次類推,長度為2的值可以轉化為2個長度為1的值之間的較小值。這樣形成一個數組dp。詳細推導參見參考2
其他
圖的遍歷演算法是基礎的基礎,它的一個作用是把圖中的資訊帶出來。學會在遞迴演算法中插入資訊以便帶出所要的資訊。
此題沒有做出來,內部資訊量著實太大了,消化消化。
dfs的巨大作用認識不到,整個題目做起來步履維艱,這個坑踩得好!
程式碼連結
https://github.com/xierensong/learngit/blob/master/hdu/2586/h2586.cpp
參考
1 https://blog.csdn.net/nameofcsdn/article/details/52230548
range-minimum-query-and-lowest-common-ancestor