题目解析 最小化团建熟悉程度和 | 豆包MarsCode AI 刷题

113 阅读7分钟

最小化团建熟悉程度和

题目链接

问题描述

小U加入了一个团队,团队决定举办分布式团建活动来加强新老成员间的交流。为了最大化新旧成员间的互动,需要将团队成员分成若干个由三人组成的小组,使得每个小组内成员间的熟悉程度之和最小

每个小组的熟悉程度定义为小组内成员两两之间的熟悉程度之和。小U负责计算出最优的分组方式。

矩阵familiar_matrix[i][j]表示第i个人和第j个人之间的熟悉程度。保证两人彼此的熟悉程度是一样的。

保证人数一定是3的倍数


测试样例

样例1:

输入:N = 3,familiar_matrix = [[100, 78, 97], [78, 100, 55], [97, 55, 100]]
输出:230

样例2:

输入:N = 6,familiar_matrix = [[100, 56, 19, 87, 38, 61], [56, 100, 70, 94, 88, 94], [19, 70, 100, 94, 43, 95], [87, 94, 94, 100, 85, 11], [38, 88, 43, 85, 100, 94], [61, 94, 95, 11, 94, 100]]
输出:299

题目解析

解决这个问题的核心在于如何高效地找到所有成员分组后最小的熟悉程度之和

难点1

怎么使用一个位掩码来表示哪些成员已经被分组,并使用一个数组来存储每个状态的最小熟悉程度之和。

核心思想

  1. 状态压缩动态规划(Bitmask DP)

    • 使用一个整数 mask 来表示当前哪些成员已经被分组。mask 的第 i 位为 1 表示第 i 个成员已经被分组,为 0 表示未被分组。
    • 使用一个数组 dp[mask] 来记录当前状态下最小的熟悉程度之和,避免重复计算。
  2. 预处理

    • 预先计算每个三人组合的熟悉程度之和,存储在一个字典或数组中,以减少在动态规划过程中的计算量。
  3. 动态规划

    • 遍历所有可能的 mask 状态,尝试将当前未分组的成员组成新的小组,并更新 dp 数组。
    • 使用剪枝技术,如果当前的熟悉程度之和已经超过了已知的最小值,则提前返回,避免不必要的计算。

具体步骤

  1. 初始化

    • 初始化 dp 数组,dp[0] = 0,表示初始状态(没有成员被分组)的熟悉程度之和为 0。
    • 其他状态的 dp 值初始化为无穷大(Long.MAX_VALUE 或 float('inf'))。
  2. 预处理

    • 遍历所有可能的三人组合,计算每个组合的熟悉程度之和,并存储在 groupFamiliarity 字典中。
  3. 动态规划

    • 遍历所有可能的 mask 状态。
    • 如果当前 mask 状态的 dp 值为无穷大,跳过该状态。
    • 找到下一个未分组的成员 nextMember
    • 尝试将 nextMember 与另外两个未分组的成员组成一个小组,更新 dp 数组。
  4. 结果

    • 最终结果为 dp[(1 << N) - 1],即所有成员都被分组时的最小熟悉程度之和。

代码实现

下面是python版本代码实现:

def solution(N, familiar_matrix):
# 初始化 dp 数组
dp = [float('inf')] * (1 << N)
dp[0] = 0
# 预处理每个小组的熟悉程度之和
group_familiarity = {}
for i in range(N):
    for j in range(i + 1, N):
        for k in range(j + 1, N):
            group_familiarity[(i, j, k)] = familiar_matrix[i][j] + familiar_matrix[i][k] + familiar_matrix[j][k]

# 动态规划
for mask in range(1 << N):
    if dp[mask] == float('inf'):
        continue

    # 找到下一个未分组的成员
    next_member = -1
    for i in range(N):
        if not (mask & (1 << i)):
            next_member = i
            break

    if next_member == -1:
        continue

    # 尝试将 next_member 与另外两个未分组的成员组成一个小组
    for j in range(next_member + 1, N):
        if not (mask & (1 << j)):
            for k in range(j + 1, N):
                if not (mask & (1 << k)):
                    new_mask = mask | (1 << next_member) | (1 << j) | (1 << k)
                    group_familiarity_sum = group_familiarity[(next_member, j, k)]
                    if dp[mask] + group_familiarity_sum < dp[new_mask]:
                        dp[new_mask] = dp[mask] + group_familiarity_sum

return dp[(1 << N) - 1]
    # 测试样例
  familiar_matrix1 = [[100, 78, 97],
[78, 100, 55],
[97, 55, 100]
]

familiar_matrix2 = [[100, 56, 19, 87, 38, 61],
[56, 100, 70, 94, 88, 94],
[19, 70, 100, 94, 43, 95],
[87, 94, 94, 100, 85, 11],
[38, 88, 43, 85, 100, 94],
[61, 94, 95, 11, 94, 100]
]

familiar_matrix3 = [[100, 2, 9, 8, 3, 12, 6, 12, 6, 18, 3, 14, 1, 9, 8, 3, 14, 4],
[2, 100, 6, 6, 7, 6, 5, 12, 7, 16, 10, 2, 5, 16, 1, 11, 7, 1],
[9, 6, 100, 14, 16, 12, 9, 1, 9, 8, 7, 2, 6, 1, 5, 5, 9, 12],
[8, 6, 14, 100, 4, 5, 10, 9, 4, 4, 10, 6, 13, 16, 9, 8, 5, 15],
[3, 7, 16, 4, 100, 8, 1, 2, 5, 11, 10, 7, 1, 1, 10, 14, 11, 4],
[12, 6, 12, 5, 8, 100, 7, 3, 15, 7, 10, 12, 4, 9, 7, 5, 15, 2],
[6, 5, 9, 10, 1, 7, 100, 7, 2, 6, 6, 6, 4, 2, 3, 10, 6, 17],
[12, 12, 1, 9, 2, 3, 7, 100, 3, 14, 12, 5, 9, 5, 7, 9, 6, 6],
[6, 7, 9, 4, 5, 15, 2, 3, 100, 11, 10, 1, 1, 15, 11, 8, 10, 5],
[18, 16, 8, 4, 11, 7, 6, 14, 11, 100, 5, 14, 5, 15, 3, 2, 1, 6],
[3, 10, 7, 10, 10, 10, 6, 12, 10, 5, 100, 1, 10, 2, 4, 4, 9, 10],
[14, 2, 2, 6, 7, 12, 6, 5, 1, 14, 1, 100, 14, 2, 13, 2, 7, 9],
[1, 5, 6, 13, 1, 4, 4, 9, 1, 5, 10, 14, 100, 1, 3, 7, 14, 12],
[9, 16, 1, 16, 1, 9, 2, 5, 15, 15, 2, 2, 1, 100, 3, 3, 7, 13],
[8, 1, 5, 9, 10, 7, 3, 7, 11, 3, 4, 13, 3, 3, 100, 1, 2, 2],
[3, 11, 5, 8, 14, 5, 10, 9, 8, 2, 4, 2, 7, 3, 1, 100, 8, 1],
[14, 7, 9, 5, 11, 15, 6, 6, 10, 1, 9, 7, 14, 7, 2, 8, 100, 4],
[4, 1, 12, 15, 4, 2, 17, 6, 5, 6, 10, 9, 12, 13, 2, 1, 4, 100]
 ]
 print(solution(3, familiar_matrix1) == 230)  # 输出: True
 print(solution(6, familiar_matrix2) == 299)  # 输出: True
 print(solution(18, familiar_matrix3))  # 输出: 387
 

java 代码使用了类似的逻辑,包括预处理和动态规划。

注意事项:

  • 类型转换:在 Java 中,int 类型的范围是有限的,因此使用 long 类型来存储 dp 数组的值,以防止溢出。
  • 字符串键:在 groupFamiliarity 字典中,使用字符串作为键,格式为 "i,j,k",以便于查找和存储三人组合的熟悉程度之和。
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static int solution(int N, int[][] familiarMatrix) {
        // 初始化 dp 数组
        long[] dp = new long[1 << N];
        for (int i = 1; i < (1 << N); i++) {
            dp[i] = Long.MAX_VALUE;
        }
        dp[0] = 0;
        // 预处理每个小组的熟悉程度之和
        Map<String, Integer> groupFamiliarity = new HashMap<>();
        for (int i = 0; i < N; i++) {
            for (int j = i + 1; j < N; j++) {
                for (int k = j + 1; k < N; k++) {
                    int sum = familiarMatrix[i][j] + familiarMatrix[i][k] + familiarMatrix[j][k];
                    groupFamiliarity.put(i + "," + j + "," + k, sum);
                }
            }
        }

        // 动态规划
        for (int mask = 0; mask < (1 << N); mask++) {
            if (dp[mask] == Long.MAX_VALUE) {
                continue;
            }

            // 找到下一个未分组的成员
            int nextMember = -1;
            for (int i = 0; i < N; i++) {
                if ((mask & (1 << i)) == 0) {
                    nextMember = i;
                    break;
                }
            }
            if (nextMember == -1) {
                continue;
            }
            // 尝试将 nextMember 与另外两个未分组的成员组成一个小组
            for (int j = nextMember + 1; j < N; j++) {
                if ((mask & (1 << j)) == 0) {
                    for (int k = j + 1; k < N; k++) {
                        if ((mask & (1 << k)) == 0) {
                            int newMask = mask | (1 << nextMember) | (1 << j) | (1 << k);
                            int groupFamiliaritySum = groupFamiliarity.get(nextMember + "," + j + "," + k);
                            dp[newMask] = Math.min(dp[newMask], dp[mask] + groupFamiliaritySum);
                        }
                    }
                }
            }
        }

        return (int) dp[(1 << N) - 1];
    }

    public static void main(String[] args) {
        int[][] familiarMatrix1 = {
            {100, 78, 97},
            {78, 100, 55},
            {97, 55, 100}
        };

        int[][] familiarMatrix2 = {
            {100, 56, 19, 87, 38, 61},
            {56, 100, 70, 94, 88, 94},
            {19, 70, 100, 94, 43, 95},
            {87, 94, 94, 100, 85, 11},
            {38, 88, 43, 85, 100, 94},
            {61, 94, 95, 11, 94, 100}
        };
        System.out.println(solution(3, familiarMatrix1) == 230);  // 输出: true
        System.out.println(solution(6, familiarMatrix2) == 299);  // 输出: true
       
    }
}