学生与导师最大兼容性配对问题详解
题目描述
有一份由 n 个问题组成的调查问卷,每个问题的答案要么是 0(否),要么是 1(是)。这份问卷被分发给了 m 名学生和 m 名导师。
students[i]
表示第 i 个学生对问卷的答案(一个长度为 n 的0/1数组)。mentors[j]
表示第 j 个导师对问卷的答案(一个长度为 n 的0/1数组)。
每个学生都会被分配给一名导师,且每位导师也被分配给一名学生,形成一一匹配。
兼容性评分定义为学生与导师答案相同的位置的个数。
例如:
学生答案:[1, 0, 1]
导师答案:[0, 0, 1]
兼容性评分 = 2 (第二和第三题答案相同)
目标是找出一种最优匹配,使得所有学生-导师配对的兼容性评分之和最大。
解题分析
本质上,这是一个最大权匹配问题:
- 有两个大小相同的集合(学生和导师)
- 边的权重为他们之间的兼容性评分
- 要找到一个完美匹配,使权重和最大
如果忽略兼容性评分,只是简单匹配,问题会简单很多。但因为权重不同,必须考虑每个配对的得分。
解题方法
1. 计算兼容性评分矩阵
首先构造一个 m×m 的矩阵 score
,其中:
score[i][j]
= 学生 i 和导师 j 答案相同的题目数量。
计算方法:
for i in range(m):
for j in range(m):
count = 0
for k in range(n):
if students[i][k] == mentors[j][k]:
count += 1
score[i][j] = count
或者简洁写法:
score[i][j] = sum(s == t for s, t in zip(students[i], mentors[j]))
2. 状态压缩 + 记忆化搜索(DFS + Memo)
定义递归函数:
dfs(i, used)
- 参数:
i
:当前正在为第 i 个学生选择导师used
:一个二进制状态,标记哪些导师已经被匹配(对应的位为 1)
- 返回值:
- 从第 i 个学生开始分配,且已经使用的导师是
used
,能获得的最大兼容性评分和
- 从第 i 个学生开始分配,且已经使用的导师是
递归思路:
- 如果
i == m
,所有学生都匹配完了,返回 0 - 否则,遍历所有未匹配的导师 j(
used & (1 << j) == 0
), - 计算匹配学生 i 和导师 j 的得分
score[i][j]
加上递归dfs(i+1, used | (1 << j))
- 取最大值返回
利用 Python 的 @lru_cache
装饰器做记忆化,避免重复计算。
代码实现
from typing import List
from functools import lru_cache
class Solution:
def maxCompatibilitySum(self, students: List[List[int]], mentors: List[List[int]]) -> int:
m, n = len(students), len(students[0])
# 计算兼容性评分矩阵
score = [[0]*m for _ in range(m)]
for i in range(m):
for j in range(m):
score[i][j] = sum(s == t for s, t in zip(students[i], mentors[j]))
@lru_cache(None)
def dfs(i, used):
if i == m:
return 0
max_score = 0
for j in range(m):
if not (used & (1 << j)):
max_score = max(max_score, score[i][j] + dfs(i+1, used | (1 << j)))
return max_score
return dfs(0, 0)
复杂度分析
- 兼容性矩阵计算:
时间复杂度为 O(m² * n),需要比较 m×m 对学生和导师,每对比较 n 个问题。 - 状态压缩搜索:
状态数为m * 2^m
,其中m
是学生数(导师数)。每个状态尝试匹配剩余的导师,整体复杂度约为 O(m² * 2^m)。
对于一般面试或竞赛中的 m
通常较小(如 8 ~ 12),此算法在可接受范围内。
示例说明
假设有:
students = [
[1, 0, 1],
[0, 0, 1]
]
mentors = [
[0, 0, 1],
[1, 0, 1]
]
兼容性矩阵计算:
学生\导师 | 导师0: [0,0,1] | 导师1: [1,0,1] |
---|---|---|
学生0: [1,0,1] | 2 | 3 |
学生1: [0,0,1] | 3 | 2 |
尝试所有匹配:
- 学生0-导师0 (2分) + 学生1-导师1 (2分) = 4
- 学生0-导师1 (3分) + 学生1-导师0 (3分) = 6
最优匹配是学生0配导师1,学生1配导师0,最大兼容性评分和为 6。
总结与比较
- 暴力全排列的时间复杂度为 O(m!),当 m 较大时不可行。
- 状态压缩 + 记忆化搜索能在保证正确性的同时,有效剪枝和复用状态,适合 m 不太大的场景。
- 如果 m 很大,还可以考虑匈牙利算法或KM算法来做最大权匹配,但实现更复杂。
这道题的关键在于:
- 正确计算每对的兼容性评分
- 构建合理的搜索策略找到最大匹配
- 使用状态压缩和记忆化避免指数爆炸
这也是匹配类问题中很典型的解法思路,掌握了这一套路,对类似题目大有裨益。