There is an undirected weighted graph with n vertices labeled from 0 to n - 1.
You are given the integer n and an array edges, where edges[i] = [ui, vi, wi] indicates that there is an edge between vertices ui and vi with a weight of wi.
A walk on a graph is a sequence of vertices and edges. The walk starts and ends with a vertex, and each edge connects the vertex that comes before it and the vertex that comes after it. It’s important to note that a walk may visit the same edge or vertex more than once.
The cost of a walk starting at node u and ending at node v is defined as the bitwise AND of the weights of the edges traversed during the walk. In other words, if the sequence of edge weights encountered during the walk is w0, w1, w2, ..., wk, then the cost is calculated as w0 & w1 & w2 & ... & wk, where & denotes the bitwise AND operator.
You are also given a 2D array query, where query[i] = [si, ti]. For each query, you need to find the minimum cost of the walk starting at vertex si and ending at vertex ti. If there exists no such walk, the answer is -1.
Return the arrayanswer, whereanswer[i]denotes the minimum cost of a walk for queryi.
Input: n =5, edges =[[0,1,7],[1,3,7],[1,2,1]], query =[[0,3],[3,4]]Output: [1,-1]Explanation:
To achieve the cost of 1in the first query, we need to move on the following edges:`0->1`(weight 7),`1->2`(weight 1),`2->1`(weight 1),`1->3`(weight 7).In the second query, there is no walk between nodes 3 and 4, so the answer is-1.
Example 2:
graph TD
A((0))
B((1))
C((2))
A ---|15| B
B ---|6| C
B ---|1| C
A ---|7| C
1
2
3
4
Input: n =3, edges =[[0,2,7],[0,1,15],[1,2,6],[1,2,1]], query =[[1,2]]Output: [0]Explanation:
To achieve the cost of 0in the first query, we need to move on the following edges:`1->2`(weight 1),`2->1`(weight 6),`1->2`(weight 1).
To calculate the cost of a path in the graph, we use bitwise AND operations across edge weights and can reuse the same edges multiple times. Performing bitwise AND on edge weights reduces or preserves costs, making it optimal to utilise edges maximally to minimise path costs. Vertices that are reachable via connected edges form groups. Within each group, any path between two nodes has the same cost, as traversing all edges in the group ensures the minimum cost, which is derived from the AND operation on all connecting edges within that group.
Union-Find is applied on the edges to group connected vertices. During the union operation, a weights array tracks the weight of each connected group. When merging two groups, the group’s weight is updated by performing a bitwise AND operation between the existing weights of the groups and the weight of the edge connecting them. This ensures that the group’s weight reflects all edges used to merge vertices. Initially, weights are set to 131071 (binary 11111111111111111) as it exceeds 10^5.
For queries:
If si and ti are the same, return 0.
If si and ti are in separate groups, return -1.
If si and ti belong to the same group, return the group’s weight, representing the AND operation of all connected edges within the group.
classUnionFind:
def __init__(self, n: int):
self.parent = list(range(n)) # Initially, each node is its own parent self.rank = [0] * n # Rank is initially 0 for all nodes self.weights = [131071] * n # Initialize weights to 131071 (binary: all 1s in 17 bits)deffind(self, x: int) -> int:
if x != self.parent[x]:
self.parent[x] = self.find(self.parent[x]) # Path compression for efficiencyreturn self.parent[x]
defunion(self, x: int, y: int, weight: int) ->None:
px = self.find(x)
py = self.find(y)
if px != py:
# Union by rank to maintain balanced treeif self.rank[px] < self.rank[py]:
self.parent[px] = py
elif self.rank[px] > self.rank[py]:
self.parent[py] = px
else:
self.parent[py] = px
self.rank[px] +=1# Update weights using bitwise AND self.weights[px] = self.weights[py] = self.weights[px] & self.weights[py] & weight
defminimum_cost_of_walk(self, x: int, y: int) -> int:
if x == y:
return0# The cost of walking to the same node is 0if self.find(x) != self.find(y):
return-1# If the nodes are in different groups, return -1return self.weights[self.find(x)] # Return the minimum cost for the connected groupclassSolution:
defminimumCost(self, n: int, edges: List[List[int]], query: List[List[int]]) -> List[int]:
uf = UnionFind(n) # Initialize UnionFind for `n` nodes# Perform union operation for each edgefor u, v, w in edges:
uf.union(u, v, w)
# Resolve each queryreturn [uf.minimum_cost_of_walk(s, t) for s, t in query]