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版本)

  • 相关阅读:
    C# 不用添加WebService引用,调用WebService方法
    贪心 & 动态规划
    trie树 讲解 (转载)
    poj 2151 Check the difficulty of problems (检查问题的难度)
    poj 2513 Colored Sticks 彩色棒
    poj1442 Black Box 栈和优先队列
    啦啦啦
    poj 1265 Area(pick定理)
    poj 2418 Hardwood Species (trie树)
    poj 1836 Alignment 排队
  • 原文地址:https://www.cnblogs.com/zyly/p/9413479.html
Copyright © 2011-2022 走看看