问题描述
小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. 策略选择
这是一个组合优化问题,需要在所有可能的分组中找到熟悉程度总和的最小值。直接暴力枚举所有可能的分组会导致计算量爆炸,因此需要优化策略:
-
位掩码+动态规划:
- 使用一个整数
mask的二进制位表示成员的分组状态,1表示未分组,0表示已分组。 - 动态规划函数递归地计算当前状态下的最小熟悉程度总和,同时通过记忆化避免重复计算。
- 使用一个整数
-
记忆化搜索:
- 利用
@lru_cache来缓存已计算的状态,大幅减少冗余递归。
- 利用
-
分治思想:
- 每次选择一个成员作为基础,与其他两名未分组成员形成小组,并递归计算剩余成员的分组方案。
2. 步骤细化
动态规划递归步骤:
- 如果
mask的所有位都为0,表示所有成员都已分组,返回0。 - 找到
mask中第一个未分组的成员first。 - 遍历所有可能的两名未分组成员
j和k:- 计算当前小组的熟悉程度之和。
- 更新状态
mask,递归计算剩余成员的最小熟悉程度。
- 记录当前
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 |
总结与反思
优点
- 动态规划结合位掩码和记忆化搜索,显著减少了冗余计算。
- 代码实现清晰易懂,逻辑紧凑。
缺点与改进
- 时间复杂度较高,在
N接近上限时(N = 21),运行速度可能变慢。 - 可以尝试使用启发式搜索(如 A*)或优化递归顺序来进一步加速。
拓展思考
此方法适用于许多分组优化问题,例如:
- 最小化旅行商问题(TSP)。
- 最大化团队协作收益等问题。