There are n
cities labeled from 1
to n
. You are given the integer n
and an array connections
where connections[i] = [city1i, city2i, costi]
indicates that the cost of connecting city city1i
and city city2i
together is costi
. You should choose some connections such that the cost of connecting all the n
cities is minimized.
Return the minimum cost to connect all the cities. If it is not possible to connect all the cities, return -1
.
The cost of connecting all cities is the sum of the costs of the connections you use.
Example 1:
Input: n = 3, connections = [[1,2,5],[1,3,6],[2,3,1]] Output: 6 Explanation: Choosing any 2 edges will connect all cities so we choose the minimum 2. The cheapest is [2,3,1] and [1,2,5] which gives a total cost of 6.
Example 2:
Input: n = 4, connections = [[1,2,3],[3,4,4]] Output: -1 Explanation: There is no way to connect all cities even if all edges are used.
Constraints:
1 <= n <= 104
1 <= connections.length <= 104
connections[i].length == 3
1 <= city1i, city2i <= n
0 <= costi <= 105
When 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 approach to connecting cities with minimum cost means we explore every possible way to build roads between cities. We check the total cost for each of these possibilities. We eventually pick the cheapest option that connects all cities.
Here's how the algorithm would work step-by-step:
def connecting_cities_with_minimum_cost_brute_force(number_of_cities, roads): minimum_cost = float('inf')
# Iterate through all possible combinations of roads.
for i in range(1 << len(roads)):
roads_subset = []
cost = 0
for j in range(len(roads)):
if (i >> j) & 1:
roads_subset.append(roads[j])
cost += roads[j][2]
# Check if the subset connects all cities.
if is_connected(number_of_cities, roads_subset):
# Update minimum cost if necessary
minimum_cost = min(minimum_cost, cost)
if minimum_cost == float('inf'):
return -1
return minimum_cost
def is_connected(number_of_cities, roads_subset):
if not roads_subset and number_of_cities > 1:
return False
parent = list(range(number_of_cities))
def find(city_index):
if parent[city_index] != city_index:
parent[city_index] = find(parent[city_index])
return parent[city_index]
def union(city_index_1, city_index_2):
root_1 = find(city_index_1)
root_2 = find(city_index_2)
parent[root_2] = root_1
for city_index_1, city_index_2, _ in roads_subset:
union(city_index_1, city_index_2)
# Check if all cities are in the same connected component.
root = find(0)
for city_index in range(1, number_of_cities):
if find(city_index) != root:
return False
return True
The problem involves finding the least expensive way to connect a set of cities using roads. Instead of trying every possible road combination, we'll build the road network gradually, always choosing the cheapest road that connects a new city to the existing network. This avoids unnecessary roads and focuses on the most cost-effective connections.
Here's how the algorithm would work step-by-step:
def minimum_cost_to_connect_cities(number_of_cities, roads): parent = list(range(number_of_cities + 1))
def find(city_node):
if parent[city_node] != city_node:
parent[city_node] = find(parent[city_node])
return parent[city_node]
def union(city_node_a, city_node_b):
root_a = find(city_node_a)
root_b = find(city_node_b)
if root_a != root_b:
parent[root_a] = root_b
return True
return False
roads.sort(key=lambda road: road[2])
minimum_cost = 0
edges_used = 0
# Iterate through sorted edges to find min cost
for city_node_a, city_node_b, road_cost in roads:
# Only connect if the cities are not already connected
if union(city_node_a, city_node_b):
minimum_cost += road_cost
edges_used += 1
# Short circuit if all nodes are connected
if edges_used == number_of_cities - 1:
break
# Check for unconnected graph
number_of_components = 0
for i in range(1, number_of_cities + 1):
if parent[i] == i:
number_of_components += 1
if number_of_components > 1:
return -1
return minimum_cost
Case | How to Handle |
---|---|
Null or empty city list | Return 0 immediately as there are no cities to connect. |
Only one city in the list | Return 0 immediately since there are no connections needed. |
All cities are already connected (cost is 0) | The algorithm should correctly identify this scenario and return 0 after initial processing if all needed connections already exist in the 'already connected' input. |
No possible way to connect all cities (disconnected graph) | Return -1 (or throw an exception) if the minimum spanning tree algorithm determines that not all cities can be connected. |
Large number of cities that causes integer overflow when summing costs | Use long or appropriate data type that can handle the potentially large sum of connection costs. |
Edge costs are negative (invalid input) | Handle invalid negative edge costs by throwing an IllegalArgumentException or similar error. |
Duplicate edges with different costs between two cities | The algorithm should choose the minimum cost edge between any two cities. |
The cost matrix is not symmetric or is malformed. | Validate the cost matrix ensuring that cost[i][j] == cost[j][i] and handle exceptions accordingly if violated. |