📜  门|门CS 2012 |第 41 题(1)

📅  最后修改于: 2023-12-03 15:42:22.747000             🧑  作者: Mango

题目描述

门 | 门CS 2012

有一个有向图,每个点上有一个权值,从起点开始,每次可以从当前点出发到任意一条出边的终点,并累加上终点的权值。注意,每个点只能经过一次,现在要求出从起点到终点的路径中,所有路径点权和最大的路径。

输入

第 1 行有两个整数,N,M,分别表示点的个数和边的个数$(1\le N\le 10000,1\le M\le 100000)$。

第 2 行有 N 个整数,表示每个点的点权。

接下来 M 行,每行三个整数,u,v,w(1≤u,v≤n,0≤w≤10000),表示 u 到 v 有 一条边,边权为 w。

输出

一个整数,表示从起点到终点的路径中,所有路径点权和最大的路径的点权和。

示例输入
5 5
1 2 3 4 5
1 2 1
1 4 4
4 5 5
2 5 2
5 3 1
示例输出
14

解题思路

本题可以借鉴一下最短路算法的思想,借助Dijkstra算法来解决最大路径问题。当然,这个最短路算法的思想要有所变更。由于Dijkstra算法中是维护的源点到各个点的最短路径,而本题需要维护源点到各个点的最大路径,因此要将贪心策略的判断条件做一些改变。我们维护一个数组 $dis$,表示从源点到各个点的最大路径,初始化为 $-\infty$。同时,我们定义一个堆 $heap$,每次将离源点最近的点加入堆中,并遍历该点所有出边,更新 $dis$ 数组中的值。注意每个点只能经过一次,因此要在更新 $dis[ v ]$ 的同时判断是否需要把 $v$ 加入堆中。另外,由于 $dis[ v ]$ 表示的是从源点到 $v$ 的最大路径,所以更新时要取 $dis[ u ]+w$ 和 $dis[ v ]$ 的最大值。

具体实现可以借用一个堆优化的Dijkstra的模板。

代码实现

import heapq

# 堆优化Dijkstra算法
def dijkstra(N: int, graph: list, start: int, end: int) -> int:
    # 用于标记每个节点是否被访问
    visited = [False] * N
    # 源点到各个点的最大路径
    dis = [-float("inf")] * N
    # 将源点到自身的路径赋值为0(源点到自己的最大路径为0)
    dis[ start ] = 0
    # 堆优化Dijkstra算法的核心:堆
    heap = [(0, start)]
    while heap:
        cur_dis, cur = heapq.heappop(heap)
        if visited[ cur ]:
            continue
        # 更新距离
        visited[ cur ] = True
        for to, w in graph[ cur ]:
            if not visited[ to ] and dis[ to ] < cur_dis+w:
                dis[ to ] = cur_dis+w
                heapq.heappush(heap, (dis[ to ], to))
    return dis[ end ]

# 主函数
if __name__ == "__main__":
    # 读入数据
    N, M = map(int, input().split())
    cost = list(map(int, input().split()))
    graph = [[] for _ in range(N)]
    for _ in range(M):
        u, v, w = map(int, input().split())
        graph[ u-1 ].append((v-1, w))
    # 执行Dijkstra算法
    print(dijkstra(N, graph, 0, N-1) + cost[0])

代码中采用了 $graph$ 数组来存储有向图的信息,其每个元素是一个列表,列表中存储了出边的终点和边权。在优化Dijkstra算法时,堆中存储的是一个二元组 $(dis[ i ], i)$,表示从源点到 $i$ 的最大路径长度和节点编号。对于每个节点 $v$,我们首先判断它是否被访问过。如果没有,则计算从源点到 $v$ 的最大路径长度,并将其加入堆中。如果已经被访问过,则忽略。以此类推,直到堆为空。在代码中,我们将节点编号从 $0\sim N-1$ 编号,需要时注意索引问题。最后输出的答案需要加上源点的点权。