You are given an undirected weighted connected graph containing n nodes labeled from 0 to n - 1, and an integer array edges where edges[i] = [ai, bi, wi] indicates that there is an edge between nodes ai and bi with weight wi.
Some edges have a weight of -1 (wi = -1), while others have a positive weight (wi > 0).
Your task is to modify all edges with a weight of -1 by assigning them positive integer values in the range [1, 2 * 109] so that the shortest distance between the nodes source and destination becomes equal to an integer target. If there are multiple modifications that make the shortest distance between source and destination equal to target, any of them will be considered correct.
Return an array containing all edges (even unmodified ones) in any order if it is possible to make the shortest distance from source to destination equal to target, or an empty array if it's impossible.
Note: You are not allowed to modify the weights of edges with initial positive weights.
Example 1:

Input: n = 5, edges = [[4,1,-1],[2,0,-1],[0,3,-1],[4,3,-1]], source = 0, destination = 1, target = 5 Output: [[4,1,1],[2,0,1],[0,3,3],[4,3,1]] Explanation: The graph above shows a possible modification to the edges, making the distance from 0 to 1 equal to 5.
Example 2:

Input: n = 3, edges = [[0,1,-1],[0,2,5]], source = 0, destination = 2, target = 6 Output: [] Explanation: The graph above contains the initial edges. It is not possible to make the distance from 0 to 2 equal to 6 by modifying the edge with weight -1. So, an empty array is returned.
Example 3:

Input: n = 4, edges = [[1,0,4],[1,2,3],[2,3,5],[0,3,-1]], source = 0, destination = 2, target = 6 Output: [[1,0,4],[1,2,3],[2,3,5],[0,3,1]] Explanation: The graph above shows a modified graph having the shortest distance from 0 to 2 as 6.
Constraints:
1 <= n <= 1001 <= edges.length <= n * (n - 1) / 2edges[i].length == 30 <= ai, bi < nwi = -1 or 1 <= wi <= 107ai != bi0 <= source, destination < nsource != destination1 <= target <= 109When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force way to modify graph edge weights involves exploring every possible weight assignment within allowed limits. We systematically try different combinations of edge weights until we find a configuration that satisfies the given conditions.
Here's how the algorithm would work step-by-step:
def modify_graph_edge_weights_brute_force(
graph,
edges_to_modify,
weight_range,
condition_function
):
best_weight_assignments = None
best_optimization_score = float('inf')
def explore_weight_assignments(index, current_weights):
nonlocal best_weight_assignments, best_optimization_score
# Base case: all edges have been assigned weights
if index == len(edges_to_modify):
# Create a modified graph with the new weights
modified_graph = graph.copy()
for i, edge in enumerate(edges_to_modify):
modified_graph[edge[0]][edge[1]] = current_weights[i]
# Check if the condition is satisfied
if condition_function(modified_graph):
# Calculate optimization score (sum of weights)
optimization_score = sum(current_weights)
# Update if this assignment is better
if optimization_score < best_optimization_score:
best_optimization_score = optimization_score
best_weight_assignments = current_weights[:]
return
# Recursive step: try all possible weights for the current edge
for weight in weight_range:
# Assign the current weight to the edge and explore
current_weights.append(weight)
explore_weight_assignments(index + 1, current_weights)
# Backtrack: remove the last assigned weight
current_weights.pop()
explore_weight_assignments(0, [])
return best_weight_assignmentsThis problem asks us to change certain links in a map to reach a target distance while making sure all distances remain as short as possible. The main idea is to first find the shortest paths without any modifications and then strategically increase the weight of a specific edge to achieve the desired total distance.
Here's how the algorithm would work step-by-step:
import heapq
def modify_graph_edge_weights(number_of_nodes, edges, source, destination, target_distance, edge_to_modify):
def calculate_shortest_distance(graph, start_node, end_node):
distances = {node: float('inf') for node in range(number_of_nodes)}
distances[start_node] = 0
priority_queue = [(0, start_node)]
while priority_queue:
current_distance, current_node = heapq.heappop(priority_queue)
if current_distance > distances[current_node]:
continue
for neighbor, weight in graph[current_node]:
distance = current_distance + weight
if distance < distances[neighbor]:
distances[neighbor] = distance
heapq.heappush(priority_queue, (distance, neighbor))
return distances[end_node]
# Build the graph using the provided edges.
graph = {node: [] for node in range(number_of_nodes)}
for u, v, weight in edges:
graph[u].append((v, weight))
graph[v].append((u, weight))
# Find the initial shortest distance.
initial_shortest_distance = calculate_shortest_distance(graph, source, destination)
# It's impossible if target is less than the initial shortest distance.
if target_distance < initial_shortest_distance:
return None
u, v, original_weight = edge_to_modify
# Restore original weights for modification purposes.
graph = {node: [] for node in range(number_of_nodes)}
for u_node, v_node, weight in edges:
graph[u_node].append((v_node, weight))
graph[v_node].append((u_node, weight))
graph[u] = [(neighbor, weight) for neighbor, weight in graph[u] if neighbor != v]
graph[v] = [(neighbor, weight) for neighbor, weight in graph[v] if neighbor != u]
graph[u].append((v, original_weight))
graph[v].append((u, original_weight))
low = original_weight
high = float('inf')
modified_weight = -1
while low <= high:
mid = (low + high) // 2
graph[u] = [(neighbor, weight) for neighbor, weight in graph[u] if neighbor != v]
graph[v] = [(neighbor, weight) for neighbor, weight in graph[v] if neighbor != u]
graph[u].append((v, mid))
graph[v].append((u, mid))
shortest_distance_with_modified_weight = calculate_shortest_distance(graph, source, destination)
if shortest_distance_with_modified_weight == target_distance:
modified_weight = mid
break
elif shortest_distance_with_modified_weight < target_distance:
high = mid - 1
else:
low = mid + 1
# If target distance isn't achievable by increasing the weight
if modified_weight == -1:
graph[u] = [(neighbor, weight) for neighbor, weight in graph[u] if neighbor != v]
graph[v] = [(neighbor, weight) for neighbor, weight in graph[v] if neighbor != u]
graph[u].append((v, original_weight))
graph[v].append((u, original_weight))
graph[u].sort()
graph[v].sort()
graph[u] = [(neighbor, weight) for neighbor, weight in graph[u] if neighbor != v]
graph[v] = [(neighbor, weight) for neighbor, weight in graph[v] if neighbor != u]
graph[u].append((v, float('inf')))
graph[v].append((u, float('inf')))
shortest_distance_with_modified_weight = calculate_shortest_distance(graph, source, destination)
if shortest_distance_with_modified_weight != target_distance:
return None
else:
modified_weight = float('inf')
modified_edges = []
for u_node, connections in graph.items():
for v_node, weight in connections:
if u_node < v_node:
modified_edges.append((u_node, v_node, weight))
for i in range(len(modified_edges)):
if (modified_edges[i][0] == u and modified_edges[i][1] == v) or (modified_edges[i][1] == u and modified_edges[i][0] == v):
modified_edges[i] = (u, v, modified_weight)
return modified_edges| Case | How to Handle |
|---|---|
| Null or empty graph input | Return an empty list or throw an exception, depending on requirements, as no edges can be modified. |
| Graph with no edges | Return an empty list, indicating no modifications are possible. |
| Graph with only one node | Return an empty list since no edges exist to modify. |
| Negative edge weights present in the graph | Ensure the algorithm correctly handles negative weights if allowed, otherwise, throw an exception if not supported. |
| Target weight impossible to achieve due to graph structure or existing weights | Return an indication that no solution is possible, such as null or an empty list. |
| Graph contains cycles, especially negative weight cycles if shortest path algorithms are involved | The algorithm must correctly handle cycles, or detect and report negative weight cycles if they invalidate shortest path assumptions. |
| Large graph where memory usage or computational complexity becomes a bottleneck | Ensure the algorithm has a reasonable time and space complexity for large graphs and avoid excessive memory allocation. |
| Integer overflow when calculating new edge weights | Use appropriate data types (e.g., long) or check for overflow before assigning the new weight to avoid incorrect results. |