列表

详情


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

 

提示:

原站题解

去查看

上次编辑到这里,代码来自缓存 点击恢复默认模板
class Solution { public: int connectTwoGroups(vector<vector<int>>& cost) { } };

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)

上一题