今天開始啦,先從一個簡單的圖論問題入手

好久沒得寫這些了,之前寫的也找不到了,因而我打算利用晚上的時間從頭寫。一方面是讓本身能更透徹的思考問題,順便練手,另一方面也是和你們有個交流機會。最近一段時間我先會寫一些經典算法的理解或者題目解題,好比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()
相關文章
相關標籤/搜索