Пример #1
0
def find_negative_cycle(
        graph: Graph, source: str) -> Tuple[bool, Optional[List[Graph.Edge]]]:
    """
    Finds a negative cycle starting at source in graph.
    :param graph: the graph where source is
    :param source: the starting node of the cycle to find
    :return:
        found: True if a negative cycle exists, False otherwise
        cycle: the list of edges representing the cycle. It is None if found is False.
    """
    # Step 1: Find all neighbors outgoing from source
    neighbors = {
        edge.opposite(source): edge
        for edge in graph.incident_edges(source, outgoing=True)
    }

    # Step 2: Find minimum edge weight in the graph
    minimum_edge_weight = min(edge.element() for edge in graph.edges())

    # Step 3: Make all edges positive
    for edge in graph.edges():
        edge._element += abs(minimum_edge_weight)

    # Step 4: Call compute_shortest_path from each vertex in neighbors to source
    paths = {
        vertex: compute_shortest_path(graph, vertex, source)
        for vertex in neighbors
    }

    # Step 5: Restore all edges weights
    for edge in graph.edges():
        edge._element -= abs(minimum_edge_weight)

    # Step 6: Close cycles
    for vertex, path in paths.items():
        if len(path) == 0:
            continue
        path_weight = sum(edge.element() for edge in path)
        first_edge = neighbors[vertex]
        first_weight = first_edge.element()
        cycle_weight = first_weight + path_weight
        cycle = [first_edge] + path
        if cycle_weight < 0:
            return True, cycle

    return False, None