PAT 1003 Emergency(25)Python

来源:互联网 发布:索尼运动耳机知乎 编辑:程序博客网 时间:2024/06/05 06:59

题目描述

1003. Emergency (25)As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.InputEach input file contains one test case. For each test case, the first line contains 4 positive integers: N (<= 500) - the number of cities (and the cities are numbered from 0 to N-1), M - the number of roads, C1 and C2 - the cities that you are currently in and that you must save, respectively. The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c1, c2 and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C1 to C2.OutputFor each test case, print in one line two numbers: the number of different shortest paths between C1 and C2, and the maximum amount of rescue teams you can possibly gather.All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.Sample Input5 6 0 21 2 1 5 30 1 10 2 20 3 11 2 12 4 13 4 1Sample Output2 4

解题思路

这道题目其实就相当于Dijkstra算法的一个变体,一般来说Djikstra算法中每个地点Node中的prev指向上一个城市的编号,而现在这个最短路径可能有多个路径,因此node的prev应该被设计成一个list。

在Dijkstra算法中根据贪心策略更新prev的时候,由于prev是一个list,所以如果weight相等的时候使用list.append,而小于的时候则直接替换整个list。

在使用Dijkstra算法计算完整个地图的最短路径之后,因为到达目标节点的最短路径可能有多条,使用了递归遍历目标节点的prev列表,计算出最短路径的条数和支援队的最大数量。

解题注意点

用Dijkstra算法用多了总会下意识地以为出发节点的下标为0,因此导致了一些计算错误,这个需要注意。

由于使用了递归计算结果,因此涉及到了闭包和nonlocal的使用。

Python nonlocal

在Python变量作用域中,首先分为全局变量和局部变量。

通俗地来说,全局变量就是在函数之外定义的变量,局部变量就是在函数之内定义的变量。

在函数内如果如果有一个变量的命名与全局变量一模一样,那么在函数内默认访问的是全局变量,但是如果该变量一旦在函数体内被赋值,那么这一个变量在整个函数体内都会作为局部变量。(如果是可变对象的一些改变操作则不影响)

# test.pya = [1, 2, 3]def f():    print(a)    a = [1, 3]    print(a)f()# resultTraceback (most recent call last):  File "/Users/crazyLeaves/Desktop/Python/PAT/temp/domain.py", line 10, in <module>    f()  File "/Users/crazyLeaves/Desktop/Python/PAT/temp/domain.py", line 6, in f    print(a)UnboundLocalError: local variable 'a' referenced before assignmentProcess finished with exit code 1

如果在函数体内修改全局变量,只需要将变量声明成global就可以了。

OK,在了解了全局变量和局部变量之后,还有第三种变量称为自由变量,自由变量主要用于嵌套函数。
以我在上述通过递归寻找最短路径数和救援队数量的方法为例:

def main():    ...    road_num = 0    rescue_num = 0    def recurisive(city_obj, res_num):        nonlocal road_num, rescue_num        if city_obj.index == src:            road_num += 1            if res_num + city_obj.rescue > rescue_num:                rescue_num = res_num + city_obj.rescue        elif city_obj.prev:            for c_index in city_obj.prev:                recurisive(cities[c_index], res_num + city_obj.rescue)

其中road_num和rescue_num变量既不属于全局作用域,也不属于recurisive函数的局部作用域。如果要在recursive函数中修改road_num和rescue_num的值,为了避免把这两个变量变为局部变量,需要通过nonlocal声明成自由变量。

其中,当recursive函数需要引用road_num和rescue_num的值的时候,可以通过recursive.__closure__属性访问。(这也是能通过闭包访问其余属性的原理)

Python PriorityQueue

在Dijkstra算法中有一个步骤是从还没有被mark的节点队列中挑选出距离最小的节点,这一步通常决定了Dijkstra算法的性能,比如说如果是线性扫描得到队列的最小值,那么时间复杂度为θ(n);如果是通过二叉堆实现的优先队列,那么得到队列的最小值并保持堆的性质需要的时间复杂度为θ(log2n)

Python标准库中的PriorityQueue本质上是通过实现了二叉堆性质的list来实现的,在Dijkstra算法中有一个坑是优先队列中不能有重复的节点,所以每一次向优先队列插入节点时需要check队列中该节点是否已经存在,而PriorityQueue并没有实现找寻特定元素的接口。

所以,我直接获取优先队列中的list并使用了in操作符线性扫描,简单粗暴。

如果希望在这一步进行优化的话,可以使用一个哈希表保存对优先队列中节点的引用,从而使时间复杂度降低。

代码

import sysimport queueclass City(object):    def __init__(self, index, rescue, distance=sys.maxsize):        self.index = index        self.rescue = rescue        self.distance = distance        self.prev = []    def __lt__(self, other):        return self.distance < other.distancedef main():    city_num, roads, src, des = [int(_) for _ in input().split()]    city_rescue = [int(_) for _ in input().split()]    cities = [City(i, city_rescue[i]) for i in range(city_num)]    cities[src].distance = 0    city_map = list()    for i in range(city_num):        city_map.append([sys.maxsize if i != j else 0 for j in range(city_num)])    for i in range(roads):        c1, c2, weight = [int(_) for _ in input().split()]        city_map[c1][c2] = weight        city_map[c2][c1] = weight    mark = [False for _ in range(city_num)]    pq = queue.PriorityQueue()    pq.put(cities[src])    for i in range(city_num):        if not pq.empty():            city = pq.get()            mark[city.index] = True            for c2, weight in enumerate(city_map[city.index]):                if weight != sys.maxsize and not mark[c2]:                    new_weight = city.distance + weight                    if new_weight == cities[c2].distance:                        cities[c2].prev.append(city.index)                        continue                    if new_weight < cities[c2].distance:                        cities[c2].distance = new_weight                        cities[c2].prev = [city.index]                        if cities[c2].index not in [q.index for q in pq.queue]:                            pq.put(cities[c2])        else:            for i in range(city_num):                if not mark[i]:                    pq.put(cities[i])                    break    road_num = 0    rescue_num = 0    def recurisive(city_obj, res_num):        nonlocal road_num, rescue_num        if city_obj.index == src:            road_num += 1            if res_num + city_obj.rescue > rescue_num:                rescue_num = res_num + city_obj.rescue        elif city_obj.prev:            for c_index in city_obj.prev:                recurisive(cities[c_index], res_num + city_obj.rescue)    recurisive(cities[des], 0)    print('{} {}'.format(road_num, rescue_num), end='')if __name__ == '__main__':    main()
原创粉丝点击