zoukankan      html  css  js  c++  java
  • 2019阿里校招测评题,光明小学完全图最短路径问题(python实现)

    题目:光明小学的小朋友们要举行一年一度的接力跑大赛了,但是小朋友们却遇到了一个难题:设计接力跑大赛的线路,你能帮助他们完成这项工作么?
    光明小学可以抽象成一张有N个节点的图,每两点间都有一条道路相连。光明小学的每个班都有M个学生,所以你要为他们设计出一条恰好经过M条边的路径。
    光明小学的小朋友们希望全盘考虑所有的因素,所以你需要把任意两点间经过M条边的最短路径的距离输出出来以供参考。

    你需要设计这样一个函数:
    res[][] Solve( N, M, map[][]);
    注意:map必然是N * N的二维数组,且map[i][j] == map[j][i],map[i][i] == 0,-1e8 <= map[i][j] <= 1e8。(道路全部是无向边,无自环)2 <= N <= 100, 2 <= M <= 1e6。要求时间复杂度控制在O(N^3*log(M))。

    map数组表示了一张稠密图,其中任意两个不同节点i,j间都有一条边,边的长度为map[i][j]。N表示其中的节点数。
    你要返回的数组也必然是一个N * N的二维数组,表示从i出发走到j,经过M条边的最短路径
    你的路径中应考虑包含重复边的情况。

    一 、求解

    先来说一下求解这一题的思路,这个问题的本质就是M步无向图的最短路径遍历,一般求解最短路径问题,我们首先想到的就是采用递归实现。

    已知有N个节点,求解从节点i到节点j之间的M步的最短路径问题,我们可以把M步分解成1步和M-1步:

    1、假设存在节点k,且k节点不同于节点i,节点j,第一步求解节点i到节点k之间的1步最短路径

    2、剩下M-1步,可以看做求解节点k到节点j之间的M-1步最短路径问题

    3、当k取不同值时,我们会得到多个距离值,选取最小的一个距离值

    # -*- coding: utf-8 -*-
    """
    Created on Fri Aug  3 08:54:35 2018
    
    @author: zy
    """
    
    import numpy  as np
    
    def Solve(maps,M):
        '''
        由N个节点两两连接组成路径,选取从节点i->节点j之间的最短M条路径
        
        param:
            maps:二维数组,由两两节点之间的路径长度组成
            M:表示所经过的路径个数
        '''    
        '''
        输入校验
        '''    
        if not isinstance(maps,(list,np.ndarray)): 
            raise ValueError('输入参数maps数据类型必须是list或者numpy.array')
        
        if len(maps.shape) != 2:
            raise ValueError('输入参数maps为二维数组')
            
        if maps.shape[0] != maps.shape[1]:
            raise ValueError('输入二维数组maps行数和列数要求一致')
            
        #计算节点的个数
        N = maps.shape[0]    
        
        if N<2 or N>100:
            raise ValueError('输入二维数组maps行数必须在2~100之间')
        
        if M<2 or M>1E6:
            raise ValueError('输入参数N的值必须在2~1e6之间')
        
        #输入二维数组数值校验
        for i in range(N):
            for j in range(i,N):
                if maps[i][j] != maps[j][i]:
                    raise ValueError('输入二维数组maps必须是对称的')
                if maps[i][j] < -1e8 or maps[i][j] > 1e8:
                    raise ValueError('二维数组maps的元素值必须在-1e8~1e8之间')
                if i==j:
                    if maps[i][j] != 0:
                        raise ValueError('二维数组maps的对角元素值必须是0')
        
        
        #用于保存i->j的路径值
        res = np.zeros_like(maps)
        
        #计算节点i->j的最短路径
        for i in range(N):
            for j in range(i,N):
                res[i][j] = MinPath(maps,M,i,j)
                res[j][i] = res[i][j]
        return res
    
    def  MinPath(maps,M,i,j):
        '''
        计算i->j的最短路径
        '''
        #递归终止条件
        if M == 1:
            return maps[i][j]
        '''计算i->j的最短路径'''
        N = maps.shape[0]   
        #用于保存i->j的可能路径长度
        length = np.zeros(N)    
        #遍历从k->j的最短路径
        for k in range(N):
            if k != i and k != j:
                #k->j的M-1条最短路径 + i->k的一条路径
                length[k] = MinPath(maps,M-1,k,j) + maps[i][k]
            
        #进行排序,过滤掉为0的值
        length = np.sort(length)
        for  i in length:
            if i != 0:
                return i
                    
                    
    if __name__ == '__main__':
        #maps = np.array([[0,2,3],[2,0,1],[3,1,0]])
        maps = np.array([[0,2,3,4],
                         [2,0,1,3],
                         [3,1,0,2],
                         [4,3,2,0]
                ])
        M = 3
        result = Solve(maps,M)
        print(result)
            

     二、时间复杂度分析

     我们来分析一下该算法的时间复杂度。

    先来分析一下递归函数MinPath(maps,M,i,j):

    1、函数的规模为M

    2、当规模是1时,函数结束

    3、假设T(M)表示规模为M的问题所需要的步骤数

    算法的递归方程为:T(M) = (N-2)T(M - 1) +3N+6

    注释:3N+6表示规模毎减少一次,所做的步骤数

    1. if M==1:                      1次
    2. N=maps.shape[0]       1次
    3. length = np.zeros(N)   1次
    4. for k in range(N):        N次

    5. if k != i and k != j:        N次
    6. + maps[i][k]                 当i==j时 N-1次,否则N-2次  我们取N-2次
    7. length = np.sort(length)  1次
    8. 最后的for循环             2次或者4次  我们取4次  

    迭代展开:T(M) = (N-2)T(M - 1) + 3N+6

    =(N-2)[(N-2)T(M-2) + 3N + 6] + 3N+6

    =(N-2)2T(M-2) + (N-2)(3N+6) + 3N+6

    =(N-2)3T(M-3) + (N-2)2(3N+6) + (N-2)(3N+6) + 3N+6

    =....

    =(N-2)(M-1) + (N-2)(M-2)(3N+6)+...+3N+6

    =(N-2)(M-1) + [(N-2)(M-1)(3N+6) - 3N-6]/(N-3)

    = O((N-2)(M-1))

    Solve函数中递归函数循环的次数为N(1+N)/2,所以算法的复杂度为O(N2(N-2)(M-1))。并没有满足题目的要求,更好的求解方法我还没有想到,有了解的大佬可以告诉小弟。

    参考文章:

    [1]递归函数时间复杂度分析(转)

    [2]2019阿里校招测评题,光明小学完全图最短路径问题(c版本)

  • 相关阅读:
    谈谈 rm -rf * 后的几点体会(年轻人得讲码德)
    shell读取文档中的命令并逐行执行
    被踢出工作群聊后的若干反思
    units命令单位转换
    想买保时捷的运维李先生学Java性能之 垃圾收集器
    想买保时捷的运维李先生学Java性能之 垃圾收集算法
    想买保时捷的运维李先生学Java性能之 生存与毁灭
    想买保时捷的运维李先生 求救求救求救求救
    想买保时捷的运维李先生学Java性能之 运行时数据区域
    想买保时捷的运维李先生学Java性能之 JIT即时编译器
  • 原文地址:https://www.cnblogs.com/zyly/p/9413479.html
Copyright © 2011-2022 走看看