【算法】Minimum Cost to Connect Two Groups of Points 连通两组点的最小成本 暴力递归

文章目录

  • Minimum Cost to Connect Two Groups of Points 连通两组点的最小成本 暴力递归
    • 问题描述:
    • 分析
    • 代码
    • Tag

Minimum Cost to Connect Two Groups of Points 连通两组点的最小成本 暴力递归

问题描述:

给你两组点,其中第一组中有 size1 个点,第二组中有 size2 个点,且 s i z e 1 > = s i z e 2 size1 >= size2 size1>=size2

任意两点间的连接成本 cost 由大小为 size1 x size2 矩阵给出,其中 cost[i][j] 是第一组中的点 i 和第二组中的点 j 的连接成本。如果两个组中的每个点都与另一组中的一个或多个点连接,则称这两组点是连通的。换言之,第一组中的每个点必须至少与第二组中的一个点连接,且第二组中的每个点必须至少与第一组中的一个点连接。

返回连通两组点所需的最小成本

size1,size2范围[1,12] ,size1>=size2 , c o s t [ i ] [ j ] cost[i][j] cost[i][j] 范围[0,100]

分析

2个数组长度为mn,不同数组元素连接的成本不一样,成本使用cost[][]表示.
要求最终每个数组元素都与另一个数组的元素建立连接,并且成本最低。
因为没有限制元素的连接次数,所以一个节点可以连接多个节点,但是至少要保证有一个连接。
为了方便讨论令arr1.length== m,arr2.length==n,m>n.
很明显要达到要求,arr2中的某元素一定会连接多个arr1中的元素。
理论上看,要达到成本最低,一定存在这样的一种方案,但是并没有一个绝对可靠的构造策略,来解决这个问题。
所以利用暴力的思路来穷举方案。
对于arr1中的元素,它可以选择arr2中的任意一个元素连接。所以可能的方案就是 2 n − 1 2^n-1 2n1,也就是从数学的角度来说,有 2 n − 1 2^n-1 2n1个组合可以完成连接arr1中的所有元素,但是这些组合只能保证至少arr2中有1个元素与arr1的所有元素,完成连接。
因此在arr2的 2 n − 1 2^n-1 2n1个组合中,会有 2 n − 2 2^n-2 2n2个组合需要把剩余未连接的元素与arr1中的元素进行连接。
而这些剩余的元素要完成连接,一定是选择其与arr1中的连接成本最低的元素进行连接。这样才可以保证组合的成本最低。
而最低的成本,一定是在这 2 n − 1 2^n-1 2n1个组合中出现的。

剩下的就是暴力枚举了,而且很明显枚举arr2的组合会更加快,你细品。
对每个arr1的元素进行枚举,同时记录下还有哪些元素在arr2的未连接过。
定义递归dfs(i,bitmask),这里选择从右向左的方向,当然你也可以从左到右。
dfs(i,bitmask) 下标i从0开始,表示arr1的0~i元素,要与arr2的所有元素建立连接,同时还有bitmask的元素状态没有连接时的最小成本。
d f s ( i , b i t m a s k S e t ) = min ⁡ d f s ( i − 1 , b i t m a s k S e t ∖ b i t k ) + c o s t [ i ] [ b i t k ] dfs(i,bitmaskSet) = \min{dfs(i-1,bitmaskSet\setminus bitk)+ cost[i][bitk]} dfs(i,bitmaskSet)=mindfs(i1,bitmaskSetbitk)+cost[i][bitk]

在每一层dfs中对arr1[i]都连接到arr2[k],然后修改剩余的bitmaskSet.
结束边界当i<0时,此时 所有arr1元素都完成连接,剩余的bitmaskset,依次连接arr1中的成本最低的元素。

d f s ( − 1 , b i t m a s k S e t ) = Σ m i n c o s t [ b i t k ] , b i t k ∈ b i t m a s k S e t dfs(-1,bitmaskSet) = \Sigma mincost[bitk] ,bitk \in bitmaskSet dfs(1,bitmaskSet)=Σmincost[bitk],bitkbitmaskSet

代码

class Solution {
    private List<List<Integer>> cost;
    private int[] mincost;
    private int[][] memo; 
    public int connectTwoGroups(List<List<Integer>> cost) {
        this.cost = cost;
        int m = cost.size(), n = cost.get(0).size();
        mincost = new int[n];
        Arrays.fill(mincost, Integer.MAX_VALUE);
        for (int j = 0; j < n; j++){
            for (var c : cost){
                mincost[j] = Math.min(mincost[j], c.get(j));
            }
                
        } 
        memo = new int[m][1 << n];
        for (int i = 0; i < m; i++){
            Arrays.fill(memo[i], -1); // -1 表示没有计算过
        }
            
        return dfs(m - 1, (1 << n) - 1);
    }

    private int dfs(int i, int bitmaskSet) {
        if (i < 0) {
            int res = 0;
            for (int k = 0; k < mincost.length; k++){
                if ((bitmaskSet >> k & 1) == 1){
                    // 第二组的点 k 未连接
                    res += mincost[k]; // 去第一组找个成本最小的点连接
                } 
            } 
            return res;
        }
        if (memo[i][bitmaskSet] != -1) return memo[i][bitmaskSet]; // 之前算过了
        int res = Integer.MAX_VALUE;
        for (int k = 0; k < mincost.length; k++){
            // 第一组的点 i 与第二组的点 k
            res = Math.min(res, dfs(i - 1, bitmaskSet & ~(1 << k)) + cost.get(i).get(k));
        } 
        return memo[i][bitmaskSet] = res; // 记忆化
    }
} 

时间复杂度O(mn2^n)

空间复杂度O(m2^n)

代码模板来源于灵神大佬,结构清晰,逻辑完整。

Tag

Bit Manipulation Bitmask

你可能感兴趣的:(数据结构与算法,算法,数据结构)