Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
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] <= 100Problem summary: 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.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Dynamic Programming · Bit Manipulation
[[15,96],[36,2]]
[[1,3,5],[4,1,1],[1,5,3]]
[[2,5,1],[3,4,7],[8,1,2],[6,2,4],[3,8,8]]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1595: Minimum Cost to Connect Two Groups of Points
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];
}
}
// Accepted solution for LeetCode #1595: Minimum Cost to Connect Two Groups of Points
func connectTwoGroups(cost [][]int) int {
m, n := len(cost), len(cost[0])
const inf = 1 << 30
f := make([][]int, m+1)
for i := range f {
f[i] = make([]int, 1<<n)
for j := range f[i] {
f[i][j] = inf
}
}
f[0][0] = 0
for i := 1; i <= m; i++ {
for j := 0; j < 1<<n; j++ {
for k := 0; k < n; k++ {
c := cost[i-1][k]
if j>>k&1 == 1 {
f[i][j] = min(f[i][j], f[i][j^(1<<k)]+c)
f[i][j] = min(f[i][j], f[i-1][j]+c)
f[i][j] = min(f[i][j], f[i-1][j^(1<<k)]+c)
}
}
}
}
return f[m][(1<<n)-1]
}
# Accepted solution for LeetCode #1595: Minimum Cost to Connect Two Groups of Points
class Solution:
def connectTwoGroups(self, cost: List[List[int]]) -> int:
m, n = len(cost), len(cost[0])
f = [[inf] * (1 << n) for _ in range(m + 1)]
f[0][0] = 0
for i in range(1, m + 1):
for j in range(1 << n):
for k in range(n):
if (j >> k & 1) == 0:
continue
c = cost[i - 1][k]
x = min(f[i][j ^ (1 << k)], f[i - 1][j], f[i - 1][j ^ (1 << k)]) + c
f[i][j] = min(f[i][j], x)
return f[m][-1]
// Accepted solution for LeetCode #1595: Minimum Cost to Connect Two Groups of Points
struct Solution;
use std::collections::HashMap;
impl Solution {
fn connect_two_groups(cost: Vec<Vec<i32>>) -> i32 {
let n = cost.len();
let m = cost[0].len();
let mask2: u32 = (1 << m) - 1;
let mut memo: HashMap<(usize, u32), i32> = HashMap::new();
Self::dp(n, mask2, &mut memo, &cost, n, m)
}
fn dp(
n1: usize,
mask2: u32,
memo: &mut HashMap<(usize, u32), i32>,
cost: &[Vec<i32>],
n: usize,
m: usize,
) -> i32 {
if let Some(&res) = memo.get(&(n1, mask2)) {
return res;
}
let res = if n1 == 0 {
(0..m)
.map(|j| {
if (mask2 & 1 << j) != 0 {
(0..n).map(|i| cost[i][j]).min().unwrap()
} else {
0
}
})
.sum::<i32>()
} else {
let mut res = std::i32::MAX;
for j in 0..m {
let next2 = mask2 & !(1 << j);
res = res.min(cost[n1 - 1][j] + Self::dp(n1 - 1, next2, memo, cost, n, m));
}
res
};
memo.insert((n1, mask2), res);
res
}
}
#[test]
fn test() {
let cost = vec_vec_i32![[15, 96], [36, 2]];
let res = 17;
assert_eq!(Solution::connect_two_groups(cost), res);
let cost = vec_vec_i32![[1, 3, 5], [4, 1, 1], [1, 5, 3]];
let res = 4;
assert_eq!(Solution::connect_two_groups(cost), res);
let cost = vec_vec_i32![[2, 5, 1], [3, 4, 7], [8, 1, 2], [6, 2, 4], [3, 8, 8]];
let res = 10;
assert_eq!(Solution::connect_two_groups(cost), res);
}
// Accepted solution for LeetCode #1595: Minimum Cost to Connect Two Groups of Points
function connectTwoGroups(cost: number[][]): number {
const m = cost.length;
const n = cost[0].length;
const inf = 1 << 30;
const f: number[][] = Array(m + 1)
.fill(0)
.map(() => Array(1 << n).fill(inf));
f[0][0] = 0;
for (let i = 1; i <= m; ++i) {
for (let j = 0; j < 1 << n; ++j) {
for (let k = 0; k < n; ++k) {
if (((j >> k) & 1) === 1) {
const c = cost[i - 1][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];
}
Use this to step through a reusable interview workflow for this problem.
Pure recursion explores every possible choice at each step. With two choices per state (take or skip), the decision tree has 2ⁿ leaves. The recursion stack uses O(n) space. Many subproblems are recomputed exponentially many times.
Each cell in the DP table is computed exactly once from previously solved subproblems. The table dimensions determine both time and space. Look for the state variables — each unique combination of state values is one cell. Often a rolling array can reduce space by one dimension.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: An incomplete state merges distinct subproblems and caches incorrect answers.
Usually fails on: Correctness breaks on cases that differ only in hidden state.
Fix: Define state so each unique subproblem maps to one DP cell.