PAT-Apat甲级题1003(python和c++实现)上

PTA | 1003 Emergency

作者 CHEN, Yue

单位 浙江大学

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.

Input Specification:

Each 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​.

Output Specification:

For 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 Input:

5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1

Sample Output:

2 4

万事开头难,先读题!

emmmmmmDFS太难了溜了

开个玩笑哈哈哈哈哈哈哈

ok热身结束,由于本题难度较大,我会尽量写的详细一点,分成上下两部分组成,上由python代码构成,下由C++代码构成。废话少说,正文开始!!!

首先读题:

作为一个城市的紧急救援队队长,你得到了一个特殊的地图。这张地图显示了几个分散的城市,由一些道路连接起来。每个城市的救援队数量和任何两个城市之间的每条道路的长度都标在地图上。当有一个紧急呼叫你从其他城市,你的工作是带领你的人尽快的地方,并在同一时间,呼吁尽可能多的手在路上。

输入规范:

每个输入文件包含一个测试用例。对于每个测试用例,第一行包含4个正整数:N(≤500)-城市数量(城市编号从0到N-1),M -道路数量,C1和C2 -当前所在的城市和必须保存的城市。下一行包含N个整数,其中第i个整数是第i个城市的救援队数量。接下来是M次输入,每个输入由三个整数c1、c2和L描述一条道路,这三个整数分别是由一条道路连接的一对城市和这条道路的长度。保证至少存在一条从C1到C2的路径。

输出规格:

对于每个测试用例,在一行中打印两个数字:C1和C2之间不同的最短路径的数量,以及您可能召集的救援队的最大数量。一行中的所有数字必须用一个空格分隔,并且行尾不允许有额外的空格。

题目很长,慢慢解析:

        - 1, 根据题目分析,描述的应该是无向带权图,将图构建出来不算太难

        - 2, 要求是从出发点到目标点,不仅需要路径最短,还需要救援队数量最大

        - 3, 输入比较简洁,常规处理就行,输出要求没有多余的空格

        - 4,一眼定真,最短路径 + 无向带权图 -> DFS

分析时间结束,现在是手搓代码时间!!!

首先,我们根据要求对输入内容进行处理:

n, m, c1, c2 = map(int, input().split())
teams = list(map(int, input().split()))

之后,由于确定方向是DFS,处理对象是无向带权图,我们直接定义:

        1, nums:用于统计最短路径数量,初始化为零

        2, matched_length:已被记录的最短路径长度,初始化为float("inf")->无穷

        3, max_team:当前的最多队伍数,初始化为0

nums = 0
matched_length = float("inf")
max_team = 0

根据图的一般处理手段,我们先初始化邻接链表road,大小为 M x M,用于记录两个相连通的路径点之间的距离,距离无穷大表示为无法到达,我们首先将其中每个元素初始化为无穷大,此后利用此数组对接下来的输入进行记录,需要注意的是:

        由于是无向带权图,lst[i][j] 与 lst[j][i] 被认为是一样的!!!!需要将这两者记录为同一值

roads = [[float("inf") for i in range(m)] for i in range(m)]
for i in range(m):
    city1, city2, road = map(int, input().split())
    roads[city1][city2] = roads[city2][city1] = road

此后,我们创建flag数组,其中第i个下标代表着第i个城市,使用0和1表示已被遍历和未被遍历,同时初始化flag[c1] = 1,表示初始点已被遍历

flags = [0 for i in range(len(teams))]
flags[c1] = 1

然后将实现本题的核心内容:DFS函数

1,根绝要求,设计dfs函数为:

        def dfs(cur_city, aim_city, cur_length, team):

其中,cur_city表示当前正在遍历的起点位置,aim_city表示目标城市,此处就是C2,cur_length表示从起点到cur_city已经走过的距离,team表示当前已经累计的救援队伍数量

变量太多,这里采取global辅助声明:

        global nums, matched_length, max_team, roads, teams, flags

2,进入dfs之后,对于当前的长度cur_city,我们将其分为以下的几种情况:

2.1 cur_city == aim_city:当然就是达到目标城市了,此时:

2.2.1 cur_length < matched_length:当前记录的路径长度小于已记录的路径长度,此时表明之前记录的所有到达aim_city的路径均不是最短路径,直接更新所有已经有过的记录,nums = 1,重新更新最短路径的数量;matched_length=cur_length,已记录的最短路径更新为当前长度;max_team= team,最短路径上的最大救援队数量更新为当前记录到的数量

2.2.2 cur_length = matched_length:表示又找到一种最短路径的方式,直接更新nums:nums += 1, 此时判断team的数量和max_team的比较,如果max_team数量小于team,则直接更新max_team

上述两种情况执行完之后,均没必要继续执行接下来的代码了,故直接选择return,结束此次dfs,2.2部分的代码如下:

    if cur_city == aim_city:
        if cur_length < matched_length:
            nums = 1
            matched_length = cur_length
            max_team = team
        elif cur_length == matched_length:
            nums += 1
            if max_team < team:
                max_team = team
        return

2.3 当cur_city并不是aim_city时,判断cur_length和matched_length的大小关系,当cur_length大于matched_length时,当前记录的路径长度已经比已记录的最短路径长度大,没必要继续dfs,结果cur_length必然大于matched_length,因此直接return,或者进行剪枝,将这部分直接忽略掉,即elif的条件直接为:

elif cur_length < matched_length:

此时,对于team中的所有点,使用循环遍历,只要当前点并未遍历,并且当前点能到达team中的那个未遍历的点(road[curcity][i] 值不为inf),将这个点的遍历状态标记为1,并将其加入dfs,此时进入dfs的参数中,curcity = i,表示第i个城市进入dfs,aim_city不变,cur_length变成cur_length + roads[i][cur_city],team的数量变为:team + teams[i],即:

dfs(i, aim_city, cur_length + roads[i][cur_city], team + teams[i])

在本轮dfs遍历结束后,需要将城市i的状态变为未遍历的状态,即flags[i] = 0,因为:

        真正的起始城市并未发生改变,城市i的变化只是为了防止dfs”回头“,当本次dfs结束后,表示本次遍历的这条路径已经处理结束,这条路上的城市应该回到初始状态,避免对接下来再次从起始点出发的其他路径造成影响。

2.3部分的代码如下:

  # 剪枝,cur_length>=matched_length时则没有继续进行的必
    elif cur_length < matched_length:要
        for i in range(len(teams)):
            # 该点未参与遍历,且相对于cur_city可达
            if flags[i] == 0 and roads[i][cur_city] < float("inf"):
                flags[i] = 1
                dfs(i, aim_city, cur_length + roads[i][cur_city], team + teams[i])
                # 回溯当前点的状态
                flags[i] = 0

以上就是本题的全部代码,完整的部分如下:
 


def dfs(cur_city, aim_city, cur_length, team):
    global nums, matched_length, max_team, roads, teams, flags
    if cur_city == aim_city:
        if cur_length < matched_length:
            nums = 1
            matched_length = cur_length
            max_team = team
        elif cur_length == matched_length:
            nums += 1
            if max_team < team:
                max_team = team
        return
    elif cur_length < matched_length:  # 剪枝,cur_length>=matched_length时则没有继续进行的必要
        for i in range(len(teams)):
            if flags[i] == 0 and roads[i][cur_city] < float("inf"):
                flags[i] = 1
                dfs(i, aim_city, cur_length + roads[i][cur_city], team + teams[i])
                flags[i] = 0

nums = 0
matched_length = float("inf")
max_team = 0

n, m, c1, c2 = map(int, input().split())
teams = list(map(int, input().split()))
roads = [[float("inf") for i in range(m)] for i in range(m)]
for i in range(m):
    city1, city2, road = map(int, input().split())
    roads[city1][city2] = roads[city2][city1] = road

flags = [0 for i in range(len(teams))]
flags[c1] = 1
dfs(c1, c2, 0, teams[c1])
print(nums, max_team, end="")

最后附上AK截图:

 写在后面:

        本题为无向带权图 + DFS的组合,本题还有其他可参考的解法,如最经典的dijkstra算法等,难度确实较大,没有头绪的小伙伴其实也不用气馁,还是要对自己有信心的!

        最后,本题所采用的方法比较简单直接和常规,如果您对本题有什么其他的见解或者其他更加优秀的解法,欢迎评论区交流,C++部分的代码将会在下一部分奉上,敬请期待hhhhhhhh!

  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值