解题报告: 最小化团建熟悉程度和 | 豆包MarsCode AI刷题

270 阅读5分钟

问题描述

小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

样例3:

输入:N = 9,familiar_matrix = [[100, 24, 35, 45, 56, 67, 78, 89, 90], [24, 100, 45, 56, 67, 78, 89, 90, 11], [35, 45, 100, 67, 78, 89, 90, 11, 12], [45, 56, 67, 100, 89, 90, 11, 12, 13], [56, 67, 78, 89, 100, 11, 12, 13, 14], [67, 78, 89, 90, 11, 100, 13, 14, 15], [78, 89, 90, 11, 12, 13, 100, 15, 16], [89, 90, 11, 12, 13, 14, 15, 100, 17], [90, 11, 12, 13, 14, 15, 16, 17, 100]]
输出:182


解题思路

1. 策略选择

这是一个组合优化问题,需要在所有可能的分组中找到熟悉程度总和的最小值。直接暴力枚举所有可能的分组会导致计算量爆炸,因此需要优化策略:

  1. 位掩码+动态规划

    • 使用一个整数 mask 的二进制位表示成员的分组状态,1 表示未分组,0 表示已分组。
    • 动态规划函数递归地计算当前状态下的最小熟悉程度总和,同时通过记忆化避免重复计算。
  2. 记忆化搜索

    • 利用 @lru_cache 来缓存已计算的状态,大幅减少冗余递归。
  3. 分治思想

    • 每次选择一个成员作为基础,与其他两名未分组成员形成小组,并递归计算剩余成员的分组方案。

2. 步骤细化

动态规划递归步骤:

  1. 如果 mask 的所有位都为 0,表示所有成员都已分组,返回0。
  2. 找到 mask 中第一个未分组的成员 first
  3. 遍历所有可能的两名未分组成员 jk
    • 计算当前小组的熟悉程度之和。
    • 更新状态 mask,递归计算剩余成员的最小熟悉程度。
  4. 记录当前 mask 状态下的最小总和。

代码实现

以下是基于 Python 的实现:

from functools import lru_cache

def solution(N, familiar_matrix):
    @lru_cache(maxsize=None)
    def dp(mask):
        if mask == 0:
            return 0
        # 找到第一个未分组的成员
        first = 0
        while not (mask & (1 << first)):
            first += 1
        # 尝试所有可能的三人组合
        min_sum = float('inf')
        for j in range(first + 1, N):
            if mask & (1 << j):
                for k in range(j + 1, N):
                    if mask & (1 << k):
                        group_sum = (
                            familiar_matrix[first][j]
                            + familiar_matrix[first][k]
                            + familiar_matrix[j][k]
                        )
                        total = group_sum + dp(
                            mask ^ (1 << first) ^ (1 << j) ^ (1 << k)
                        )
                        min_sum = min(min_sum, total)
        return min_sum

    # 初始状态:所有成员未分组
    full_mask = (1 << N) - 1
    return dp(full_mask)

示例

示例1

输入:

3
100 78 97
78 100 55
97 55 100

输出:

230

解释: 唯一的分组是 {1, 2, 3},熟悉程度总和为 78 + 97 + 55 = 230


示例2

输入:

6
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, 3, 5} 和 {2, 4, 6},熟悉程度总和为 19+43+38 + 56+94+94 = 299


时间复杂度分析

时间复杂度

  • 状态总数:每个成员两种状态(分组或未分组),共 2^N 种状态。
  • 每种状态的操作:最多遍历所有未分组成员组合(O(N^3))。
  • 总复杂度O(2^N × N^3)

空间复杂度

  • 状态缓存O(2^N)
  • 递归深度O(N)
  • 总空间复杂度为 O(2^N)

测试样例

输入样例输出
N = 3, familiar_matrix = [[100, 78, 97], [78, 100, 55], [97, 55, 100]]230
N = 6, familiar_matrix = [[100, 56, 19, 87, 38, 61], ...]299
N = 9, familiar_matrix = [[100, 24, 35, 45, ...]182

总结与反思

优点

  1. 动态规划结合位掩码和记忆化搜索,显著减少了冗余计算。
  2. 代码实现清晰易懂,逻辑紧凑。

缺点与改进

  1. 时间复杂度较高,在 N 接近上限时(N = 21),运行速度可能变慢。
  2. 可以尝试使用启发式搜索(如 A*)或优化递归顺序来进一步加速。

拓展思考

此方法适用于许多分组优化问题,例如:

  1. 最小化旅行商问题(TSP)。
  2. 最大化团队协作收益等问题。