好久沒得寫這些了,之前寫的也找不到了,因而我打算利用晚上的時間從頭寫。一方面是讓本身能更透徹的思考問題,順便練手,另一方面也是和你們有個交流機會。最近一段時間我先會寫一些經典算法的理解或者題目解題,好比ACM、一些公司面試題目、平時在工做中容易使用到的,也有機會來進行一些問題的討論。
若是是題解,我會註明題目的出處,有興趣能夠嘗試解題並提交代碼。題目解題我會優先選用Python編寫,部分不支持Python的Online Judge我選用C++。
限於本身的水平,有錯誤的地方還請指正python
言歸正傳,先從一個簡單的題目入手:面試
<題目來源:百度2017秋招,http://exercise.acmcoder.com/... >算法
在一個無向圖G=(V,E)中,定義頂點s到頂點t的特徵距離是構成從頂點s->t的最短路徑(shortest path)的邊集中E'= {e1,e2...en}中最長的邊的大小。目標是要找到這個全部最短路徑中最大的特徵距離。app
問題比較明確了,最直接的解決問題思路是直接求出s->t的全部最短路徑,再遍歷全部的最短路徑找出最長的邊便可。測試
首先考慮選擇單源點最短路徑算法:
(1)Dijkstra
(2)SPFA優化
該圖是一個不含有負權變的無向圖,上面兩種算法均可以採用,這裏選擇SPFA,這個算法的算法核心就是對隊列中的頂點進行鬆弛操做:
dist[u] = min{dist[u], dist[v] + edge(u, v).wight}
其實質是若是到達u的最短路徑能夠由到達頂點v的最短路徑加上鍊接u,v邊的權獲得,而且比原來的路徑更短,那麼咱們就能夠用這條新的路徑長度來更新到達u的最短路徑,這樣的操做稱之爲鬆弛操做。而後咱們在整個圖中不斷嘗試尋找這樣的頂點進行鬆弛操做,直到不能再發現能夠鬆弛的頂點。spa
那麼考慮能不能在SPFA鬆弛獲得dist[v]的過程當中記錄到達頂點v的最長的邊max_edge[v]呢?那麼這樣在一次SFPA後,在求得最短路徑的同時也獲得了題目所求的最長的邊。code
分析上面的鬆弛操做後,發現並不容易實現,緣由是,對頂點u的任何一次鬆弛操做都不能肯定edge(u,v)是否最終在到達u的最短路徑中。因爲每次須要獲取最大的邊,須要不斷保留最大的那條邊。一旦某個邊再也不是構成最短路徑上的邊的時候,那麼這個結果就錯誤了。在鬆弛過程當中去存儲所有路徑不是一個明智的選擇。orm
咱們在SPFA結束後,根據dist[]能夠求出全部的最短路徑
若是dist[u] == dist[v] + edge(u, v),那麼v必然是從源點到u的最短路徑上的一個點。問題就彷佛變得比較簡單,咱們能夠從源點開始,來一個DFS,只須要是知足上面這個表達式的邊,咱們就對其進行擴展,再深搜這個邊鏈接的頂點。當頂點搜索到咱們的終點時,就得到一條從s->t的最路徑,當DFS結束後能夠得到全部路徑。那麼只須要找出其餘最大的就是答案,若是不可達,直接輸出No answer隊列
問題至此已經獲得解決,使用python提交,1513ms,感受速度慢了一些
分析了下,多是最後在DFS全部的最短路徑的時候消耗了比較多的時間
因爲咱們並不關心具體的最短路徑,也不關心這樣的最短路徑有多少條,而是隻關心其中的最長的那條邊,而這條邊必然是全部邊中的一條,遂想到枚舉全部的邊,而後測試該邊是否在最短路徑中,仍然須要使用dist[u] == dist[v] + edge(u, v)這個條件,可是,若是肯定edge(u, v)就在到t的最短路徑中呢?
考慮連結這條邊的兩個頂點u,v,若是dist[s->u] + edge(u, v).wight + dist[v->t]=dist[s->t]的話,顯然edge(u,v)這邊邊在s->t的最短路徑中
其中dist[s->u]就是dist[u],edge(u, v)已知,dist[s->t]就是dist[t],因爲咱們所求出的dist都是以s做爲源點的,而dist[v->t]須要以v爲源點,而且每次枚舉不一樣的頂點v'都須要計算不一樣的dist[v'->t],顯然行不通,可是咱們也發現一個特色是,咱們每次都是計算終點爲t的最短路徑,若是咱們把終點t做爲源點,把全部邊反向作一次SPFA,就只須要計算一次,就能夠獲得全部的dist[v'->t],本題是個無向圖,處理起來更方便
後面提交,發現效率並無顯著提高,又改成SPFA+heap的方式,仍然沒有提升,懷疑是我採用的python的輸入方式所致使,中午休息的時候順手拍了一個CPP的版的,並無使用heap,而後只須要12ms,可見差距仍是比較大。
下面是最後採用SPFA+heap優化的代碼,DFS和枚舉邊的方式都有寫:
import heapq const_idx_vertex = 0 const_idx_wight = 1 def dfs(u, t, vertexes, dist, path, edge_l, res): if u == t: #print path r = 0 for l in edge_l: r = max(r, l) res.append(r) return for vertex in vertexes[u]: v = vertex[const_idx_vertex] w = vertex[const_idx_wight] if dist[u] + w == dist[v]: path.append(v) edge_l.append(w) dfs(v, t, vertexes, dist, path, edge_l, res) path.pop() edge_l.pop() def spfa(dist, src, n, vertexes): que = [] in_que = [False for i in range(n + 1)] que.append(src) in_que[src] = True dist[src] = 0 heap = [] heapq.heappush(heap, (0, src)) while len(heap) > 0: info = heapq.heappop(heap) u = info[1] in_que[u] = False for vertex in vertexes[u]: v = vertex[const_idx_vertex] w = vertex[const_idx_wight] if dist[v] > dist[u] + w or dist[v] < 0: dist[v] = dist[u] + w if not in_que[v]: heapq.heappush(heap, (dist[v], v)) in_que[v] = True def get_max_edge(dist, dist_rev, vertexes, t, n): ans = -1 for u in range(1, n + 1): for vertex in vertexes[u]: v = vertex[const_idx_vertex] w = vertex[const_idx_wight] if dist[u] != -1 and dist_rev[v] != -1 and dist[u] + w + dist_rev[v] == dist[t]: ans = max(ans, w) return ans if ans > 0 else "No answer" def main(): t_cases = int(raw_input()) for t_case in range(t_cases): temp = raw_input().split(' ') n = int(temp[0]) m = int(temp[1]) s = int(temp[2]) t = int(temp[3]) vertexes = [[] for i in range(n + 1)] for i in range(m): temp = raw_input().split(' ') u = int(temp[0]) v = int(temp[1]) w = int(temp[2]) vertexes[u].append([v, w]) vertexes[v].append([u, w]) dist = [-1 for i in range(n + 1)] ''' # Before improved the code res = [] path = [s] edge_l = [] ans = -1 spfa(dist, s, n, vertexes) if dist[t] > 0: dfs(s, t, vertexes, dist, path, edge_l, res) for re in res: ans = max(re, ans) if ans < 0: ans = 'No answer' print 'Case #{}: {}'.format(t_case + 1, ans) ''' dist_rev = [-1 for i in range(n + 1)] spfa(dist, s, n, vertexes) spfa(dist_rev, t, n, vertexes) print 'Case #{}: {}'.format(t_case + 1, get_max_edge(dist, dist_rev, vertexes, t, n)) if __name__ == '__main__': main()