今天开始啦,先从一个简单的图论问题入手

很久没得写这些了,以前写的也找不到了,于是我打算利用晚上的时间从头写。一方面是让自己能更透彻的思考问题,顺便练手,另外一方面也是和大家有个交流机会。最近一段时间我先会写一些经典算法的理解或者题目解题,比如ACM、一些公司面试题目、平时在工作中容易使用到的,也有机会来进行一些问题的讨论。
如果是题解,我会注明题目的出处,有兴趣可以尝试解题并提交代码。题目解题我会优先选用Python编写,部分不支持Python的Online Judge我选用C++。
限于自己的水平,有错误的地方还请指正


言归正传,先从一个简单的题目入手:

特征距离

<题目来源:百度2017秋招,http://exercise.acmcoder.com/... >

问题描述:

在一个无向图G=(V,E)中,定义顶点s到顶点t的特征距离是构成从顶点s->t的最短路径(shortest path)的边集中E'= {e1,e2...en}中最长的边的大小。目标是要找到这个所有最短路径中最大的特征距离。

问题比较明确了,最直接的解决问题思路是直接求出s->t的所有最短路径,再遍历所有的最短路径找出最长的边即可。

首先考虑选择单源点最短路径算法:
(1)Dijkstra
(2)SPFA

该图是一个不含有负权变的无向图,上面两种算法都可以采用,这里选择SPFA,这个算法的算法核心就是对队列中的顶点进行松弛操作:
dist[u] = min{dist[u], dist[v] + edge(u, v).wight}
其实质是如果到达u的最短路径可以由到达顶点v的最短路径加上连接u,v边的权得到,并且比原来的路径更短,那么我们就可以用这条新的路径长度来更新到达u的最短路径,这样的操作称之为松弛操作。然后我们在整个图中不断尝试寻找这样的顶点进行松弛操作,直到不能再发现可以松弛的顶点。

那么考虑能不能在SPFA松弛得到dist[v]的过程中记录到达顶点v的最长的边max_edge[v]呢?那么这样在一次SFPA后,在求得最短路径的同时也得到了题目所求的最长的边。

分析上面的松弛操作后,发现并不容易实现,原因是,对顶点u的任何一次松弛操作都不能确定edge(u,v)是否最终在到达u的最短路径中。由于每次需要获取最大的边,需要不断保留最大的那条边。一旦某个边不再是构成最短路径上的边的时候,那么这个结果就错误了。在松弛过程中去存储全部路径不是一个明智的选择。

我们在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,可见差距还是比较大。

今天开始啦,先从一个简单的图论问题入手_第1张图片

下面是最后采用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()

你可能感兴趣的:(图论,最短路径)