最小的必要团队
难度:
标签:
题目描述
代码结果
运行时间: 59 ms, 内存: 0.0 MB
/*
* 思路:
* 1. 使用位操作和Stream流进行技能组合的匹配和最小团队计算。
* 2. 用Map存储技能与位之间的映射,用stream操作对people列表进行处理。
* 3. 使用reduce方法实现动态规划数组的更新。
*/
import java.util.*;
import java.util.stream.*;
public class Solution {
public int[] smallestSufficientTeam(String[] req_skills, List<List<String>> people) {
int n = req_skills.length;
Map<String, Integer> skillMap = IntStream.range(0, n)
.boxed()
.collect(Collectors.toMap(i -> req_skills[i], i -> i));
List<Integer>[] dp = new List[1 << n];
dp[0] = new ArrayList<>();
IntStream.range(0, people.size()).forEach(i -> {
int skillMask = people.get(i).stream()
.map(skillMap::get)
.reduce(0, (acc, x) -> acc | (1 << x));
IntStream.range(0, dp.length).forEach(j -> {
if (dp[j] == null) return;
int newMask = j | skillMask;
if (dp[newMask] == null || dp[newMask].size() > dp[j].size() + 1) {
dp[newMask] = new ArrayList<>(dp[j]);
dp[newMask].add(i);
}
});
});
List<Integer> result = dp[(1 << n) - 1];
return result.stream().mapToInt(i -> i).toArray();
}
}
解释
方法:
这个问题可以通过动态规划求解,利用位掩码来表示技能集。使用一个整数的不同位来代表不同的技能,如果某位为1,则表示拥有该技能。首先,为每个技能建立一个映射,将技能映射到一个索引(即位位置)。然后为每个人创建一个位掩码,表示他们拥有的技能。对于每个技能,维护一个集合,包含所有拥有该技能的人员索引。定义一个递归函数 dp(i, hit),其中 i 表示当前考虑的技能索引,hit 是一个位掩码,表示已覆盖的技能集。如果当前技能已被覆盖,即(hit >> i) & 1 为真,则直接处理下一个技能。否则,尝试通过添加拥有当前技能的人来更新 hit。最终,递归的目标是找到覆盖所有技能的最小团队。使用 @cache 装饰器来缓存递归结果,避免重复计算。
时间复杂度:
O(n * 2^n * 60)
空间复杂度:
O(2^n * n)
代码细节讲解
🦆
在动态规划中的状态定义`dp(i, hit)`,参数`i`和`hit`具体代表什么?
▷🦆
为什么在动态规划的递归函数中,使用位掩码`hit`来表示已覆盖的技能集?
▷🦆
在递归函数`dp(i, hit)`中,如何决定是否需要继续递归到下一个技能?
▷🦆
在解决方案中提到了使用`min`函数和`key=len`来选择团队,这种方法的效率如何?是否存在更优的策略?
▷