Minimum Cost to Connect Two Groups of Points
HARDDescription
You are given two groups of points where the first group has size1 points, the second group has size2 points, and size1 >= size2.
The cost of the connection between any two points are given in an size1 x size2 matrix where cost[i][j] is the cost of connecting point i of the first group and point j of the second group. The groups are connected if each point in both groups is connected to one or more points in the opposite group. In other words, each point in the first group must be connected to at least one point in the second group, and each point in the second group must be connected to at least one point in the first group.
Return the minimum cost it takes to connect the two groups.
Example 1:
Input: cost = [[15, 96], [36, 2]] Output: 17 Explanation: The optimal way of connecting the groups is: 1--A 2--B This results in a total cost of 17.
Example 2:
Input: cost = [[1, 3, 5], [4, 1, 1], [1, 5, 3]] Output: 4 Explanation: The optimal way of connecting the groups is: 1--A 2--B 2--C 3--A This results in a total cost of 4. Note that there are multiple points connected to point 2 in the first group and point A in the second group. This does not matter as there is no limit to the number of points that can be connected. We only care about the minimum total cost.
Example 3:
Input: cost = [[2, 5, 1], [3, 4, 7], [8, 1, 2], [6, 2, 4], [3, 8, 8]] Output: 10
Constraints:
size1 == cost.lengthsize2 == cost[i].length1 <= size1, size2 <= 12size1 >= size20 <= cost[i][j] <= 100
Approaches
Checkout 2 different approaches to solve Minimum Cost to Connect Two Groups of Points. Click on different approaches to view the approach and algorithm in detail.
Top-Down DP with Optimized Connection Search
This approach also uses top-down dynamic programming but optimizes the transition step. In the previous approach, for each point i in group 1, we iterated through all 2^size2 possible connection subsets, leading to a high time complexity. We can optimize this search for the best connection subset for point i.
Algorithm
- Define a DP state
dp[mask]as the minimum cost to connect the firstipoints of group 1 such that the points in group 2 represented bymaskare covered. - We can use a 1D DP array
dpof size2^size2and iterate fromi = 1tosize1. Thedparray at the beginning of iterationiwill store the results for the firsti-1points. - For each point
ifrom group 1, we need to update thedptable. Letdp_prevbe the table fori-1points anddp_currbe the table foripoints. - The transition is
dp_curr[mask] = min_{prev_mask | p_mask = mask, p_mask > 0} (dp_prev[prev_mask] + cost_for_p_mask). Here,cost_for_p_maskis the cost to connect pointi-1to the subsetp_maskof group 2. - This transition is an OR-convolution in min-plus algebra. A naive implementation takes
O(4^size2)time. We can optimize this using subset convolution techniques. - The transition can be rewritten as
dp_curr[mask] = min_{sub ⊂ mask, sub>0} (dp_prev[mask \ sub] + cost_for_sub). This is a subset convolution. - Subset convolution can be computed efficiently using Sum over Subsets (SOS) DP. The overall complexity for computing one
dp_currtable fromdp_prevbecomesO(size2^2 * 2^size2). - After iterating through all
size1points, the final answer is not yetdp[(1<<size2)-1]. The DP state only ensures that the firstsize1points are connected. We still need to ensure all points in group 2 are connected. - A better approach is to use the Top-Down DP from the previous method but optimize the transition for connecting point
i. - Instead of iterating through all
2^size2subsets for pointi, we can build the connection subset forirecursively. Define a helper functionfindMinForI(i, mask, j, p_mask)that finds the minimum cost by deciding whether to connectitojor not, and recursing forj+1. This avoids theO(4^size2)complexity. - The state for this helper function can be memoized. For a fixed
(i, mask)from the outer recursion, the helper's state is(j, p_mask), leading to anO(size2 * 2^size2)transition.
We can optimize the transition solve(i, mask) by creating a helper function that builds the optimal connection for point i more efficiently. Instead of a brute-force loop over 2^size2 subsets, this helper function, let's call it findMinForI, will decide for each point j in group 2 whether to include it in point i's connection set or not.
The helper function findMinForI(j, p_mask) would compute the minimum cost for connecting point i to a subset of points {j, ..., size2-1} given that i is already connected to p_mask from points {0, ..., j-1}. The state of this helper function is (j, p_mask), where j is the current point in group 2 being considered, and p_mask is the subset of connections for point i built so far.
findMinForI(j, p_mask):
- Base Case: When
j == size2, we have considered all points in group 2 for connection toi. Ifp_maskis empty, it meansiwas not connected, which is invalid (return infinity). Otherwise, the total cost issolve(i + 1, mask | p_mask). - Recursive Step: For point
j, we have two choices:- Don't connect
itoj: The cost isfindMinForI(j + 1, p_mask). - Connect
itoj: The cost iscost[i][j] + findMinForI(j + 1, p_mask | (1 << j)).
- Don't connect
- The function returns the minimum of these two choices.
The main solve(i, mask) function will call findMinForI(0, 0) to find the best connection for point i. The helper function findMinForI can be memoized on its state (j, p_mask). Since this helper is called within solve(i, mask), its memoization table must be reset for each state (i, mask) of the outer recursion.
This optimization reduces the complexity of the transition. For each state (i, mask), we solve a subproblem of size O(size2 * 2^size2). The total complexity becomes O(size1 * size2 * 2^size2).
class Solution {
private int size1, size2;
private List<List<Integer>> cost;
private int[] minCostG2;
private Integer[][] memo;
private Integer[][] memo_helper;
public int minCostConnectTwoGroups(List<List<Integer>> cost) {
this.cost = cost;
this.size1 = cost.size();
this.size2 = cost.get(0).size();
this.minCostG2 = new int[size2];
for (int j = 0; j < size2; j++) {
int minC = Integer.MAX_VALUE;
for (int i = 0; i < size1; i++) {
minC = Math.min(minC, cost.get(i).get(j));
}
minCostG2[j] = minC;
}
this.memo = new Integer[size1][1 << size2];
return solve(0, 0);
}
private int solve(int i, int mask) {
if (i == size1) {
int remainingCost = 0;
for (int j = 0; j < size2; j++) {
if ((mask & (1 << j)) == 0) {
remainingCost += minCostG2[j];
}
}
return remainingCost;
}
if (memo[i][mask] != null) {
return memo[i][mask];
}
// Memoization for the helper function, specific to the state (i, mask)
// Although 'i' and 'mask' are not passed to findMinForI, they define the context
// in which it's called. The recursive calls from findMinForI depend on them.
memo_helper = new Integer[size2 + 1][1 << size2];
int res = findMinForI(i, mask, 0, 0);
return memo[i][mask] = res;
}
private int findMinForI(int i, int mask, int j, int p_mask) {
if (j == size2) {
if (p_mask == 0) {
return 1_000_000_000; // Invalid path, point i not connected
}
return solve(i + 1, mask | p_mask);
}
if (memo_helper[j][p_mask] != null) {
return memo_helper[j][p_mask];
}
// Option 1: Don't connect point i to point j
int res = findMinForI(i, mask, j + 1, p_mask);
// Option 2: Connect point i to point j
res = Math.min(res, cost.get(i).get(j) + findMinForI(i, mask, j + 1, p_mask | (1 << j)));
return memo_helper[j][p_mask] = res;
}
}
Complexity Analysis
Pros and Cons
- Significantly more efficient than the brute-force transition.
- Feasible for the given constraints on
size1andsize2.
- The implementation is more complex, involving a recursive helper function with its own memoization table within the main recursive function.
- Care must be taken with managing the memoization tables for both the main and helper functions.
Code Solutions
Checking out 3 solutions in different languages for Minimum Cost to Connect Two Groups of Points. Click on different languages to view the code.
class Solution {
public
int connectTwoGroups(List<List<Integer>> cost) {
int m = cost.size(), n = cost.get(0).size();
final int inf = 1 << 30;
int[][] f = new int[m + 1][1 << n];
for (int[] g : f) {
Arrays.fill(g, inf);
}
f[0][0] = 0;
for (int i = 1; i <= m; ++i) {
for (int j = 0; j < 1 << n; ++j) {
for (int k = 0; k < n; ++k) {
if ((j >> k & 1) == 1) {
int c = cost.get(i - 1).get(k);
f[i][j] = Math.min(f[i][j], f[i][j ^ (1 << k)] + c);
f[i][j] = Math.min(f[i][j], f[i - 1][j] + c);
f[i][j] = Math.min(f[i][j], f[i - 1][j ^ (1 << k)] + c);
}
}
}
}
return f[m][(1 << n) - 1];
}
}
Video Solution
Watch the video walkthrough for Minimum Cost to Connect Two Groups of Points
Similar Questions
5 related questions you might find useful
Patterns:
Data Structures:
Subscribe to Scale Engineer newsletter
Learn about System Design, Software Engineering, and interview experiences every week.
No spam, unsubscribe at any time.