A regional government wants to improve their existing infrastructure between a collection of towns T. In specific, the government want to build a minimum number of roads such that there is a route from each town to each other town. The government has been advised by a dubious consultant that in the resulting road network, the number of users of a given road is independent of the presence of alternative routes.
The regional government wants to minimise the number of roads it has to built to ensure that one can travel from one town to the other.
Furthermore, the government wants to maximize the benefits of the road network by maximizing the number of users of the roads built. Hence, the government wants to only build roads that are expected to be used often. To help the construction plans, the government has asked the dubious consultant to estimate, for each pair of cities, the number of road users that would use the road between these two cities (if that road was built).
Now the regional government is looking for a construction plan for a minimum number of roads connecting all towns that see the highest total usage among them.
P1.1
Model the above problem as a graph problem: What are the nodes and edges in your graph, do the edges have weights, and what problems are you trying got answer on your graph?
Nodes: Each town in the set of town T is represented as a node in the graph. Denote the set of nodes as V
Edges: Each potential road can be built between two pair of town. Denote it as E. Each edge weights represents the estimated number of users who would use the road should it is constructed. Denote the weight of an edge between nodes i and j as w(i,j).
The problem can then be modelled as given a weighted undirected graph G=(V,E) representing towns and potential roads, find a maximum weight minimum spanning tree of G.
Explanation:
We need to ensure there is a route from each town to every other town while minimising the number of roads being built, thus the minimum spanning tree of the graph.
Among all possible MSTs, we want to find the one with the maximum total edge weight, thus maximum weight MSTs.
P1.2
Provide an algorithm ConstructionPlan to find the minimum number of roads to build. Explain why your algorithm is correct.
Let w(x,y) be the weight of the edge between nodes x and y.
Algorithm 40 ConstructionPlan(G,T)
Require: Graph G=(T,E) with nodes T representing towns and weighted edges E representing potential roads with weights as estimated road usage
Ensure: Set of edges E′ representing roads to build for maximum weight minimum spanning tree
1:Adj←new list[∣T∣] ▷Adjacency list for graph
2:for (u,v,w)∈E do
3:Adj[u].append((v,w))
4:Adj[v].append((u,w))
5:end for
6:E′←∅
7:Pick an arbitrary node s∈T
8:V←{s}
9:Q←new min-priority queue ▷Priority queue of edges
10:for (v,w)∈Adj[s] do
11:Q.insert((s,v,w))
12:end for
13:while ∣V∣<∣T∣ do
14:(u,v,w)←Q.extract_max() ▷Get max weight edge
15:if v∈/V then
16:E′←E′∪(u,v)
17:V←V∪v ▷Mark v as visited
18:for (x,w)∈Adj[v] do
19:if x∈/V then
20:Q.insert((v,x,w))
21:end if
22:end for
23:end if
24:end while
25:return E′
This is the Prim’s algorithm for finding MSTs. We instead replace the minimum weight with maximum weights to fit with problem description in P1.1
Correctness:
Invariant: V contains all visited nodes,E′ contains edges of a maximum weight spanning tree over nodes in V
At L4, invariant holds since V={s} and E′=∅
per iteration, we add E′ with the maximum weight edge (u,v) from any visited nodes u∈V to any unvisited node v∈T∖V (L6-14). Then add v to V (L15), maintaining the invariant because:
Vnew=V∪{v}
Enew′=E′∪{(u,v)} is a maximum weight spanning tree over Vnew due to (u,v) is the maximum weight edge connecting V to T∖V.
The algorithm never create cycle in E′ since we only add edge from visited nodes to unvisited nodes, which cannot create a cycle.
Bound function:
Let w(E′) be the total weight of edges in E′. Per iteration, w(E′) is the maximum possible weight of any spanning tree over the nodes in V.
This holds initially when V={s} and w(E′)=0. Per iteration, we add the maximum weight edge (u,v) to E′ from V to T∖V. Bound function is maintained because:
w(Enew′)=w(E′)+w(u,v)≥w(E′)
Any other spanning tree T′ over V∪{v} must contain an edge (x,y) from V to {v}, and w(x,y)≤w(u,v). Therefore, w(T′)≤w(E′∪{(u,v)})
The loop terminates when ∣V∣=∣T∣, i.e all nodes are visited.
Thus the algorithm is correct. □
P1.3
Explain which graph representation you used for your algorithm and what the complexity of your algorithm is using this graph representation.
Uses adjacency list representation of the graph G=(T,E). Since we store a list of edges for each nodes, or for each node u∈T, we maintain a list Adj[u] containing the node v and weight w(u,v) for every edge (u,v)∈E.
The time complexity of the algorithm is O(∣T∣2log∣T∣+∣E∣):
Initialising adjacency list: O(∣E∣), where the adjacency list takes O(∣E∣) to populate
L4: Run for ∣T∣ iterations
Inside the loop, find maximum weight edge takes from Q takes O(log∣T∣). Therefore each iteration the while loop takes O(∣T∣log∣T∣)
Adding extracted edge to E′ takes O(1)
Thus, the overall time complexity is O(∣T∣2log∣T∣+∣E∣). If the graph is complete, then ∣E∣=2∣T∣(∣T∣−1)=O(∣T∣2), and the time complexity is O(∣T∣2log∣T∣)
The space complexity is O(∣T∣+∣E∣)
P1.4
What is the worst-case complexity of your solution if you use the other graph representation? Explain your answer
Worst case complexity of the algorithm using adjacency matrix representation is O(∣T∣2log∣T∣).
If adjacency matrix representation is used, or we use a ∣T∣×∣T∣ matrix Adj to represent the graph, where Adj[u][v]=w for an edge (u,v) with weight w.
Initialising the matrix will take O(∣T∣2) time.
Adding edges from starting node s to priority queue Q will take O(∣T∣log∣T∣) time, as we need to scan row Adj[s] which has ∣T∣ elements.
Inside the while loop, for loop (L18-23) that iterates over the edges of the newly visited node v now takes O(∣T∣log∣T∣) time, as we need to scane the row that has ∣T∣ entries, and for each unvisited node, we insert the edge into Q which takes O(log∣T∣) time. Therefore, the while loop will take O(∣T∣⋅(∣T∣log∣T∣))=O(∣T∣2log∣T∣)
Thus, the total time complexity would be O(∣T∣2+∣T∣2log∣T∣)=O(∣T∣2log∣T∣)
Whereas the space complexity is O(∣T∣2). Could be more efficient for sparse graph where ∣E∣≪∣T∣2.
Problème 2.
A directed graph G=(N,E,s,t) with s∈N the source and t∈N the target is a series-parallel graph if it can be constructed inductively using the following rules:
An elementary series-parallel graph is a single edge from s to t
The series-construction. Let G1=(N1,E1,s1,t1) and G2=(N2,E2,s2,t2) be two series-parallel graphs with the node c in common (N1∩N2={c}). The graph G=(N1∪N2,E1∪E2) is a series-parallel graph
The parallel-construction. Let G1=(N1,E1,s1,t1) and G2=(N2,E2,s2,t2) be two series-parallel graphs without nodes in common (N1∩N2=∅) and let s,t∈/(N1∪N2) be two fresh nodes. The graph G=(N1∪N2∪{s,t},E1∪E2∪{(s,s1),(s,s2),(t1,t),(t2,t)}) is a series-parallel graph.
Now assume we have a series-parallel graph G=(N,E,s,t) with an edge-weight function weight:E→Z (here, Z are the integers, which includes negative numbers). We note that series-parallel graphs are relatively simple structures.
P2.1
Write an algorithm to compute the single-source shortest paths from the source s to all nodes n∈N in O(∣N∣+∣E∣) time
Algorithm 41 ShortestPathsSP(G=(N,E,s,t), weight)
1:dist← ▷Initialize empty dictionary for distances
2:if G is an elementary graph (single edge (s,t)) then
3:dist[s]←0
4:dist[t]←weight(s,t)
5:else if G is a series composition of G1 and G2 then
6:dist1←ShortestPathsSP(G1,weight)
7:dist2←ShortestPathsSP(G2,weight)
8:dist←dist1∪dist2 ▷Combine distances
9:for each node n in G2 do
10:dist[n]←dist[n]+dist1[t1]
11:end for
12:else if G is a parallel composition of G1 and G2 then
13:dist1←ShortestPathsSP(G1,weight)
14:dist2←ShortestPathsSP(G2,weight)
15:dist←dist1∪dist2 ▷Combine distances
16:dist[s]←0
17:dist[t]←min(dist1[t1],dist2[t2])
18:end if
19:return dist
P2.2
Explain why your algorithm is correct
Base case: For an elementary graph with one edge (s,t), it sets dist[s]=0 and dist[t]=weight(s,t), which is the shortest path from s to t.
If G is a series-composition: Let G1 and G2 be the two series-parallel graphs with common node c.
It recursively computes the shortest paths from s to all nodes in G1 and G2, store them in dist1 and dist2 respectively.
For any node n∈G2, the shortest path from s \mu st go through target t1 of G1. Thus, we update dist[n]=dist[n]+dist1[t1].
If G is a parallel-composition: Let G1 and G2 be the two series-parallel graphs with new source s and target t
It recursively computes the shortest paths from s to all nodes in G1 and G2, store them in dist1 and dist2 respectively.
The new source s has distance 0, correctly set by dist[s]=0
The new target t has distance min(dist1[t1],dist2[t2]), which is the shortest path from s to t.
In both cases, it combines the distance using union operation, and the algorithm is correct. □
P2.3
Explain which graph representation you used for your algorithm and why your algorithm has the stated complexity
It uses adjacency list representation of the graph G=(N,E,s,t). Each node maintains a list of its outgoing edges. Since in series-parallel graphs, each node has at most 2 outgoing edges, the adjacency list representation is efficient. Additionally, the algorithm recursively traverse the graph, and finding outgoing edges takes constant time.
For base case, the algorithm takes O(1) time to set the distance for the elementary graph.
In both cases, it calls itself on G1 and G2, taking O(∣N1∣+∣E1∣) and O(∣N2∣+∣E2∣) time respectively. Then it updates the distance for each node in G2 in O(∣N2∣) time. Thus, the time complexity is O(∣N1∣+∣E1∣+∣N2∣+∣E2∣)
Since each node is visited exactly once during the traversal, the time complexity is O(∣N∣+∣E∣) (since ∣N1∣+∣N2∣=∣N∣ and ∣E1∣+∣E2∣=∣E∣)
P2.4
What is the worst-case complexity of your solution if you use the other graph representation? Explain your answer
If using adjacency matrix representation, the worst-case complexity of the algorithm is O(∣N∣2).
It will take O(∣N∣2) time to initialise the adjacency matrix.
The analysis of the algorithm remains the same. The difference here is that finding outgoing edges for a node will take O(∣N∣) time in worst-case, as it will have to traverse the entire row of the matrix corresponding to that node. Since the ops is performed on each node during traversal, total complexity would be O(∣N∣2).