We are provided with a graph G = (V, E), where each edge is associated with some positive weight (W[i] > 0). We are asked to find the shortest path from A to B provided we follow the following conditions :
We are also given some forbidden paths, say x. The goal is the shortest path should not contain any forbidden path as its sub-path.
For example: Consider a graph G with 4 vertices and 5 edges (1, 2, 1), (2, 1, 1), (1, 4, 5), (1, 3, 1.5), (3, 4, 1). Here, the 3rd entity in the bracket denotes the weight of edge between 'u' and 'v'. We are required to find the shortest path between 1 and 4, and the list of forbidden paths contains only the path 1->3->4.
The paths from 1 to 4 are 1 -> 4, 1 -> 2 -> 3 -> 4, 1 -> 3 -> 4.
Out of these, only 1st 2 are valid paths, and among them the shortest path is 1 -> 2 -> 3 -> 4 of total weight as 3.0.
Is there an efficient algorithm for the above task? Please describe the algorithm with the complexity analysis. I would be highly thankful if you could provide a code as well.
You can preprocess the graph in such a way to embed forbidden paths in-to it. For every forbidden path you duplicate vertices which belong to it and then drop some of the edges. That duplicates would have special meaning: walking along duplicated edge would mean that you have come to a vertex along the forbidden path and you can't walk along the last edge of it. If you are walking along original edges, then you have come somewhere to the middle of forbidden path so it does not affect you. To achieve that you drop all incoming edges to a duplicate path excepting edge to it's second vertex from it's first vertex. But you drop that edge from original path.
a forYou split vertices which are part of forbidden paths in-to several virtual vertices and drop some of the edges. Let's suppose that in following graph path ABC is forbidden:
A-->B-->C
D->/
Then you split B to BA and BD (depending from which vertex you have come to B) and drop BA->C edge.
A->BA /->C
D->BD-/
Now you can use classic dijkstra on that preprocessed graph.
More complex example, let's suppose that ABCF is forbidden:
G->\
A-->B-->C-->F
D->/ \->E
So we duplicate B and C as internal vertices of the forbidden route, we drop A->B edge and leave only A->B'. We also drop all other incoming edges to B' and C' but we leave B'->E edge because it drives away from forbidden route. We also drop C'->F edge.
/->B'-->C'
| \------->\
| \
| G->\ \
A B-->C---->F \
D->/ \----------->E
Related
I have been practicing graph questions lately.
https://leetcode.com/problems/course-schedule-ii/
https://leetcode.com/problems/alien-dictionary/
The current way I detect cycles is to use two hashsets. One for visiting nodes, and one for fully visited nodes. And I push the result onto a stack with DFS traversal.
If I ever visit a node that is currently in the visiting set, then it is a cycle.
The code is pretty verbose and the length is long.
Can anyone please explain how I can use a more standard top-sort algorithm (Kahn's) to detect cycles and generate the top sort sequence?
I just want my method to exit or set some global variable which flags that a cycle has been detected.
Many thanks.
Khan's algorithm with cycle detection (summary)
Step 1: Compute In-degree: First we create compute a lookup for the in-degrees of every node. In this particular Leetcode problem, each node has a unique integer identifier, so we can simply store all the in-degrees values using a list where indegree[i] tells us the in-degree of node i.
Step 2: Keep track of all nodes with in-degree of zero: If a node has an in-degree of zero it means it is a course that we can take right now. There are no other courses that it depends on. We create a queue q of all these nodes that have in-degree of zero. At any step of Khan's algorithm, if a node is in q then it is guaranteed that it's "safe to take this course" because it does not depend on any courses that "we have not taken yet".
Step 3: Delete node and edges, then repeat: We take one of these special safe courses x from the queue q and conceptually treat everything as if we have deleted the node x and all its outgoing edges from the graph g. In practice, we don't need to update the graph g, for Khan's algorithm it is sufficient to just update the in-degree value of its neighbours to reflect that this node no longer exists.
This step is basically as if a person took and passed the exam for
course x, and now we want to update the other courses dependencies
to show that they don't need to worry about x anymore.
Step 4: Repeat: When we removing these edges from x, we are decreasing the in-degree of x's neighbours; this can introduce more nodes with an in-degree of zero. During this step, if any more nodes have their in-degree become zero then they are added to q. We repeat step 3 to process these nodes. Each time we remove a node from q we add it to the final topological sort list result.
Step 5. Detecting Cycle with Khan's Algorithm: If there is a cycle in the graph then result will not include all the nodes in the graph, result will return only some of the nodes. To check if there is a cycle, you just need to check whether the length of result is equal to the number of nodes in the graph, n.
Why does this work?:
Suppose there is a cycle in the graph: x1 -> x2 -> ... -> xn -> x1, then none of these nodes will appear in the list because their in-degree will not reach 0 during Khan's algorithm. Each node xi in the cycle can't be put into the queue q because there is always some other predecessor node x_(i-1) with an edge going from x_(i-1) to xi preventing this from happening.
Full solution to Leetcode course-schedule-ii in Python 3:
from collections import defaultdict
def build_graph(edges, n):
g = defaultdict(list)
for i in range(n):
g[i] = []
for a, b in edges:
g[b].append(a)
return g
def topsort(g, n):
# -- Step 1 --
indeg = [0] * n
for u in g:
for v in g[u]:
indeg[v] += 1
# -- Step 2 --
q = []
for i in range(n):
if indeg[i] == 0:
q.append(i)
# -- Step 3 and 4 --
result = []
while q:
x = q.pop()
result.append(x)
for y in g[x]:
indeg[y] -= 1
if indeg[y] == 0:
q.append(y)
return result
def courses(n, edges):
g = build_graph(edges, n)
ordering = topsort(g, n)
# -- Step 5 --
has_cycle = len(ordering) < n
return [] if has_cycle else ordering
I got this exercise :
Give an example of three different directed graphs on V = {1, 2, 3}
where the edges (1, 2) and (1, 3) exist. Write both the adjacency-list and adjacency-
matrix representation for each.
I find only this two:
G = {(1,2), (1,3)}
G = {(1,2), (1,3), (2,3)}
What I miss? Something like that is valid: G = {(1,2), (1,3), (3,2)} ?
It's a directed graph, which means all links are one-way. If you want to be able to go from 1 to 2 and from 2 to 1, you need two links, (1,2) and (2,1) - that's part of the definition of a directed graph.
With that, enumerate all possible links for a graph of 3 vertexes:
(1,2)
(2,1)
(1,3)
...
Once you enumerate all possible links in such a graph, you can pick and choose unique sets of those links to make into multiple graphs, subject to the constraints given to you by the exercise.
For instance, here are a couple graphs:
{(1,2)}
{(2,1)}
{(1,3)}
{(3,1)}
{(3,2), (2,1), (1,3)}
...
You already have two out of three requested answers and you need a third graph to complete the exercise. You need to give answers that include two provided links. Why not give as an answer a graph that has every link in it? A graph of every link must contain the two requested links, right?
Consider the following example graph:
Given the vertices A, B and C (creators), how to figure out their common neighbors?(projects all 3 participated in)For two vertices, I could simply use GRAPH_COMMON_NEIGHBORS("myGraph", A, B), but what if I want to query for 3 or more? Expected result: 1 and 2.
Given the same vertices, how can I make it return common neighbors with no other connections?(creators exclusively participated in a project, no additional edges allowed)?Expected result: 1, because 2 has an edge coming from D, which isn't one of the starting vertices.
You can simply pass the same set of vertices as both parameters for common neighbors. Then repack the result in a better format for AQL to compute the intersection:
let res = (
let nodes = ["a/A","a/B","a/C"]
for n in GRAPH_COMMON_NEIGHBORS("g",nodes , nodes)
for f in VALUES(n)
return VALUES(f)
)
return CALL("intersection", res[0])
I have a directed, unweighted, possibly cyclic graph that can contain loops and multiple duplicate edges (i.e. two edges from node 1 to node 2).
I would now like to find the length of the longest trail in this graph, i.e. the longest path that:
- uses no edge twice (but if there are multiple edges from node 1 to node 2, it can use every one of them)
- possibly visits nodes several time (i.e. it does not have to be a simple path)
In particular, is this problem NP-hard? I know that the longest simple path is NP-hard (reducing Hamiltonian Path to it) and the longest trail with edge reusal is in P (Bellman ford with weight -1 on every edge). However, with this problem, I am not quite sure and I could not find good information on it.
Although I am not completely sure, I think that this problem is NP-hard. As I understand, your question arises due to multiple edges between nodes. The graphs that has multiple edges between same nodes can be expanded to larger graphs with no multiple edges between them. Thus, a graph with multiple edges between same nodes has no difference than a graph without multiple edges.
Let me walkthrough a simple example to explain:
Let there be a graph with 3 nodes (A,B,C) and 5 edges between them (A to B, A to B, B to A, B to C, C to A)
This graph can be expanded and shown with 5 nodes and 7 edges.
Lets expand the node A to 3 different nodes (A1, A2, A3). When we adjust the edges according to previous edges, there exists 7 edges(A1 to B, A2 to B, B to A3, B to C, C to A1, C to A2, C to A3)
As a result, now we have a graph without multiple edges and can be evaluated with the help of Hamiltonian and Bellman Ford.
Hope I've at least cleared the problem a bit.
I was wondering if I can modify Dijkstra’a Alghorithm in this way:
Let’s say there are 2 paths between two vertices with following lengths:
5, 8, 6, 9 // sum=28
2, 4, 8, 25 //sum=39
First path is shorter but after ignoring the longest edge in both I get the following sums: 19 and 14, so I choose the second path.
Maybe there is different way to solve it, without using Dijkstra?
I'm not sure is this idea working, but on first it seems to me like it can.
For each visited node, beside distance D(n), store length of longest edge on the path to it, say L(v). Distance of unvisited neighbouring node is min D(n) + L(n) - max(L(n), weight(v,n)), for all neighbours n that are visited. L(v) of next visited node is max(L(n), weight(v,n)), if n is last node on a path to the v. If there are more paths to v (more n's) with same length, than choose one with largest L(v).