1947. 最大兼容性评分和

学生与导师最大兼容性配对问题详解

题目描述

有一份由 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 == 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]23
学生1: [0,0,1]32

尝试所有匹配:

  • 学生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算法来做最大权匹配,但实现更复杂。

这道题的关键在于:

  • 正确计算每对的兼容性评分
  • 构建合理的搜索策略找到最大匹配
  • 使用状态压缩和记忆化避免指数爆炸

这也是匹配类问题中很典型的解法思路,掌握了这一套路,对类似题目大有裨益。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值