1595. 连通两组点的最小成本
给你两组点,其中第一组中有 size1
个点,第二组中有 size2
个点,且 size1 >= size2
。
任意两点间的连接成本 cost
由大小为 size1 x size2
矩阵给出,其中 cost[i][j]
是第一组中的点 i
和第二组中的点 j
的连接成本。如果两个组中的每个点都与另一组中的一个或多个点连接,则称这两组点是连通的。换言之,第一组中的每个点必须至少与第二组中的一个点连接,且第二组中的每个点必须至少与第一组中的一个点连接。
返回连通两组点所需的最小成本。
示例 1:
输入:cost = [[15, 96], [36, 2]] 输出:17 解释:连通两组点的最佳方法是: 1--A 2--B 总成本为 17 。
示例 2:
输入:cost = [[1, 3, 5], [4, 1, 1], [1, 5, 3]] 输出:4 解释:连通两组点的最佳方法是: 1--A 2--B 2--C 3--A 最小成本为 4 。 请注意,虽然有多个点连接到第一组中的点 2 和第二组中的点 A ,但由于题目并不限制连接点的数目,所以只需要关心最低总成本。
示例 3:
输入:cost = [[2, 5, 1], [3, 4, 7], [8, 1, 2], [6, 2, 4], [3, 8, 8]] 输出:10
提示:
size1 == cost.length
size2 == cost[i].length
1 <= size1, size2 <= 12
size1 >= size2
0 <= cost[i][j] <= 100
原站题解
golang 解法, 执行用时: 4 ms, 内存消耗: 2.3 MB, 提交时间: 2023-06-20 09:32:27
func connectTwoGroups(cost [][]int) int { m := len(cost[0]) f := make([]int, 1<<m) for j := 0; j < m; j++ { mn := math.MaxInt for _, c := range cost { mn = min(mn, c[j]) } bit := 1 << j for mask := 0; mask < bit; mask++ { f[bit|mask] = f[mask] + mn } } for _, row := range cost { for j := 1<<m - 1; j >= 0; j-- { res := math.MaxInt for k, c := range row { // 第一组的点 i 与第二组的点 k res = min(res, f[j&^(1<<k)]+c) } f[j] = res } } return f[1<<m-1] } func min(a, b int) int { if b < a { return b }; return a }
java 解法, 执行用时: 8 ms, 内存消耗: 39.3 MB, 提交时间: 2023-06-20 09:32:06
class Solution { public int connectTwoGroups(List<List<Integer>> cost) { int m = cost.get(0).size(); var f = new int[1 << m]; for (int j = 0; j < m; j++) { int mn = Integer.MAX_VALUE; for (var c : cost) mn = Math.min(mn, c.get(j)); int bit = 1 << j; for (int mask = 0; mask < bit; mask++) f[bit | mask] = f[mask] + mn; } for (var row : cost) { var r = row.toArray(); // 转成数组效率更高 for (int j = (1 << m) - 1; j >= 0; j--) { int res = Integer.MAX_VALUE; for (int k = 0; k < m; k++) // 第一组的点 i 与第二组的点 k res = Math.min(res, f[j & ~(1 << k)] + (int) r[k]); f[j] = res; } } return f[(1 << m) - 1]; } }
python3 解法, 执行用时: 600 ms, 内存消耗: 16.2 MB, 提交时间: 2023-06-20 09:31:45
# 递推优化 class Solution: def connectTwoGroups(self, cost: List[List[int]]) -> int: f = [0] * (1 << len(cost[0])) for j, mn in enumerate(map(min, zip(*cost))): bit = 1 << j for mask in range(bit): f[bit | mask] = f[mask] + mn for row in cost: for j in range(len(f) - 1, -1, -1): f[j] = min(f[j & ~(1 << k)] + c for k, c in enumerate(row)) return f[-1]
golang 解法, 执行用时: 8 ms, 内存消耗: 4.5 MB, 提交时间: 2023-06-20 09:31:19
func connectTwoGroups(cost [][]int) int { n, m := len(cost), len(cost[0]) minCost := make([]int, m) for j := 0; j < m; j++ { minCost[j] = math.MaxInt for _, c := range cost { minCost[j] = min(minCost[j], c[j]) } } f := make([][]int, n+1) for i := range f { f[i] = make([]int, 1<<m) } for j := 0; j < 1<<m; j++ { for k, c := range minCost { if j>>k&1 == 1 { // 第二组的点 k 未连接 f[0][j] += c // 去第一组找个成本最小的点连接 } } } for i, row := range cost { for j := 0; j < 1<<m; j++ { res := math.MaxInt for k, c := range row { // 第一组的点 i 与第二组的点 k res = min(res, f[i][j&^(1<<k)]+c) } f[i+1][j] = res } } return f[n][1<<m-1] } func min(a, b int) int { if b < a { return b }; return a }
java 解法, 执行用时: 62 ms, 内存消耗: 40.3 MB, 提交时间: 2023-06-20 09:31:05
class Solution { public int connectTwoGroups(List<List<Integer>> cost) { int n = cost.size(), m = cost.get(0).size(); var minCost = new int[m]; Arrays.fill(minCost, Integer.MAX_VALUE); for (int j = 0; j < m; j++) for (var c : cost) minCost[j] = Math.min(minCost[j], c.get(j)); var f = new int[n + 1][1 << m]; for (int j = 0; j < 1 << m; j++) for (int k = 0; k < m; k++) if ((j >> k & 1) == 1) // 第二组的点 k 未连接 f[0][j] += minCost[k]; // 去第一组找个成本最小的点连接 for (int i = 0; i < n; i++) { for (int j = 0; j < 1 << m; j++) { int res = Integer.MAX_VALUE; for (int k = 0; k < m; k++) // 第一组的点 i 与第二组的点 k res = Math.min(res, f[i][j & ~(1 << k)] + cost.get(i).get(k)); f[i + 1][j] = res; } } return f[n][(1 << m) - 1]; } }
python3 解法, 执行用时: 540 ms, 内存消耗: 16.4 MB, 提交时间: 2023-06-20 09:30:51
# 1:1递推 class Solution: def connectTwoGroups(self, cost: List[List[int]]) -> int: n, m = len(cost), len(cost[0]) min_cost = [min(col) for col in zip(*cost)] # 每一列的最小值 f = [[0] * (1 << m) for _ in range(n + 1)] for j in range(1 << m): f[0][j] = sum(c for k, c in enumerate(min_cost) if j >> k & 1) for i, row in enumerate(cost): for j in range(1 << m): f[i + 1][j] = min(f[i][j & ~(1 << k)] + c for k, c in enumerate(row)) # 第一组的点 i 与第二组的点 k return f[n][-1]
golang 解法, 执行用时: 12 ms, 内存消耗: 4.4 MB, 提交时间: 2023-06-20 09:30:21
func connectTwoGroups(cost [][]int) int { n, m := len(cost), len(cost[0]) minCost := make([]int, m) for j := 0; j < m; j++ { minCost[j] = math.MaxInt for _, c := range cost { minCost[j] = min(minCost[j], c[j]) } } memo := make([][]int, n) for i := range memo { memo[i] = make([]int, 1<<m) for j := range memo[i] { memo[i][j] = -1 // -1 表示没有计算过 } } var dfs func(int, int) int dfs = func(i, j int) (res int) { if i < 0 { for k, c := range minCost { if j>>k&1 == 1 { // 第二组的点 k 未连接 res += c // 去第一组找个成本最小的点连接 } } return } p := &memo[i][j] if *p != -1 { // 之前算过了 return *p } res = math.MaxInt for k, c := range cost[i] { // 第一组的点 i 与第二组的点 k res = min(res, dfs(i-1, j&^(1<<k))+c) } *p = res // 记忆化 return *p } return dfs(n-1, 1<<m-1) } func min(a, b int) int { if b < a { return b }; return a }
java 解法, 执行用时: 23 ms, 内存消耗: 40.1 MB, 提交时间: 2023-06-20 09:30:08
class Solution { private List<List<Integer>> cost; private int[] minCost; private int[][] memo; public int connectTwoGroups(List<List<Integer>> cost) { this.cost = cost; int n = cost.size(), m = cost.get(0).size(); minCost = new int[m]; Arrays.fill(minCost, Integer.MAX_VALUE); for (int j = 0; j < m; j++) for (var c : cost) minCost[j] = Math.min(minCost[j], c.get(j)); memo = new int[n][1 << m]; for (int i = 0; i < n; i++) Arrays.fill(memo[i], -1); // -1 表示没有计算过 return dfs(n - 1, (1 << m) - 1); } private int dfs(int i, int j) { if (i < 0) { int res = 0; for (int k = 0; k < minCost.length; k++) if ((j >> k & 1) == 1) // 第二组的点 k 未连接 res += minCost[k]; // 去第一组找个成本最小的点连接 return res; } if (memo[i][j] != -1) return memo[i][j]; // 之前算过了 int res = Integer.MAX_VALUE; for (int k = 0; k < minCost.length; k++) // 第一组的点 i 与第二组的点 k res = Math.min(res, dfs(i - 1, j & ~(1 << k)) + cost.get(i).get(k)); return memo[i][j] = res; // 记忆化 } }
python3 解法, 执行用时: 500 ms, 内存消耗: 27.9 MB, 提交时间: 2023-06-20 09:29:46
# 动态规划 # dfs(i, j) 第一组0..i与第二组0..m-1相连,且第二组集合j未被连接,最小成本 class Solution: def connectTwoGroups(self, cost: List[List[int]]) -> int: n, m = len(cost), len(cost[0]) min_cost = [min(col) for col in zip(*cost)] # 每一列的最小值 @cache # 记忆化搜索 def dfs(i: int, j: int) -> int: if i < 0: return sum(c for k, c in enumerate(min_cost) if j >> k & 1) return min(dfs(i - 1, j & ~(1 << k)) + c for k, c in enumerate(cost[i])) # 第一组的点 i 与第二组的点 k return dfs(n - 1, (1 << m) - 1)