作为项目经理,你规划了一份需求的技能清单 req_skills,并打算从备选人员名单 people 中选出些人组成一个「必要团队」( 编号为 i
的备选人员 people[i] 含有一份该备选人员掌握的技能列表)。
所谓「必要团队」,就是在这个团队中,对于所需求的技能列表 req_skills
中列出的每项技能,团队中至少有一名成员已经掌握。可以用每个人的编号来表示团队中的成员:
- 例如,团队 team = [0, 1, 3]表示掌握技能分别为people[0],people[1],和people[3]的备选人员。
请你返回 任一 规模最小的必要团队,团队成员用人员编号表示。你可以按 任意顺序 返回答案,题目数据保证答案存在。
示例 1:
**输入:** req_skills = ["java","nodejs","reactjs"], people = [["java"],["nodejs"],["nodejs","reactjs"]]
**输出:** [0,2]
示例 2:
**输入:** req_skills = ["algorithms","math","java","reactjs","csharp","aws"], people = [["algorithms","math","java"],["algorithms","math","reactjs"],["java","csharp","aws"],["reactjs","csharp"],["csharp","math"],["aws","java"]]
**输出:** [1,2]
提示:
- 1 <= req_skills.length <= 16
- 1 <= req_skills[i].length <= 16
- req_skills[i]由小写英文字母组成
- req_skills中的所有字符串 互不相同
- 1 <= people.length <= 60
- 0 <= people[i].length <= 16
- 1 <= people[i][j].length <= 16
- people[i][j]由小写英文字母组成
- people[i]中的所有字符串 互不相同
- people[i]中的每个技能是- req_skills中的技能
- 题目数据保证「必要团队」一定存在
方法一:动态规划
思路与算法
题目输入数组 req_skills 的长度最大为 16,req_skills 中的每一项,被选择或者不被选择,总共的组合情况为 2^{16 种。因此可以通过「状态压缩」来表示一个技能集合。
我们将每一个技能 req_skills}[i] 映射到一个二进制数的第 i 位。例如:
- req_skills}[0] 用 2^{0} = (1 << 0) = 1 来表示。
- req_skills}[1] 用 2^{1} = (1 << 1) = 2 来表示。
以此类推,如此一来我们就可以用一个数字来表示一个技能集合。同时两个集合的并集计算,就可以转化为两个整数的或运算。
我们采用自下而上的「动态规划」的思路来解题,用 dp}[i] 来表示状态,状态含义是满足技能集合为 i 的最小人数的数组。初始化状态是 dp}[0],为空数组,因为如果不需要任何技能,不用任何人就可以完成。
我们首先依次遍历 peoples,求出当前这个人所有的技能集合 cur_skill。然后遍历 dp 表中的结果 dp}[\textit{prev}],其中原来的技能集合用 prev 来表示。设加入当前这个人后新的技能集合是 comb,由原来的技能集合和当前技能集合求并集后,可以得到:comb} = \textit{prev} | \textit{cur_skill。状态转移的规则是,如果 dp}[\textit{comb}] 不存在,或 dp}[\textit{prev}] 的长度加上 1 小于 dp}[\textit{comb}].\textit{size}(),那么我们就需要更新 dp}[\textit{comb}] 为 dp}[\textit{prev}],再将当前人加入到 dp}[\textit{comb}]。这里我们更新 dp}[\textit{comb}] 时候,可以采用直接覆盖的方式,因为更新后的结果因为已经包含了当前员工的技能,所以不会再次满足转移规则,而发生重复转移。
最后,所有技能的集合用 (1 << n) - 1 来表示,其中 n 是 req_skills 的长度,我们只需要返回最终答案 dp}[(1 << n) - 1]。
代码
[sol1-Java]| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 
 | class Solution {public int[] smallestSufficientTeam(String[] req_skills, List<List<String>> people) {
 int n = req_skills.length, m = people.size();
 HashMap<String, Integer> skill_index = new HashMap<>();
 for (int i = 0; i < n; ++i) {
 skill_index.put(req_skills[i], i);
 }
 List<Integer>[] dp = new List[1 << n];
 dp[0] = new ArrayList<>();
 for (int i = 0; i < m; ++i) {
 int cur_skill = 0;
 for (String s : people.get(i)) {
 cur_skill |= 1 << skill_index.get(s);
 }
 for (int prev = 0; prev < dp.length; ++prev) {
 if (dp[prev] == null) {
 continue;
 }
 int comb = prev | cur_skill;
 if (dp[comb] == null || dp[prev].size() + 1 < dp[comb].size()) {
 dp[comb] = new ArrayList<>(dp[prev]);
 dp[comb].add(i);
 }
 }
 }
 return dp[(1 << n) - 1].stream().mapToInt(i -> i).toArray();
 }
 }
 
 | 
 [sol1-C++]| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 
 | class Solution {public:
 vector<int> smallestSufficientTeam(vector<string>& req_skills, vector<vector<string>>& people) {
 int n = req_skills.size(), m = people.size();
 unordered_map<string, int> skill_index;
 for (int i = 0; i < n; ++i) {
 skill_index[req_skills[i]] = i;
 }
 vector<vector<int>> dp(1 << n);
 for (int i = 0; i < m; ++i) {
 int cur_skill = 0;
 for (string& s : people[i]) {
 cur_skill |= 1 << skill_index[s];
 }
 for (int prev = 0; prev < dp.size(); ++prev) {
 if (prev > 0 && dp[prev].empty()) {
 continue;
 }
 int comb = prev | cur_skill;
 if (comb == prev) {
 continue;
 }
 if (dp[comb].empty() || dp[prev].size() + 1 < dp[comb].size()) {
 dp[comb] = dp[prev];
 dp[comb].push_back(i);
 }
 }
 }
 return dp[(1 << n) - 1];
 }
 };
 
 | 
 [sol1-C#]| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 
 | public class Solution {public int[] SmallestSufficientTeam(string[] req_skills, IList<IList<string>> people) {
 int n = req_skills.Length, m = people.Count;
 IDictionary<string, int> skill_index = new Dictionary<string, int>();
 for (int i = 0; i < n; ++i) {
 skill_index.Add(req_skills[i], i);
 }
 IList<int>[] dp = new IList<int>[1 << n];
 dp[0] = new List<int>();
 for (int i = 0; i < m; ++i) {
 int cur_skill = 0;
 foreach (string s in people[i]) {
 cur_skill |= 1 << skill_index[s];
 }
 for (int prev = 0; prev < dp.Length; ++prev) {
 if (dp[prev] == null) {
 continue;
 }
 int comb = prev | cur_skill;
 if (dp[comb] == null || dp[prev].Count + 1 < dp[comb].Count) {
 dp[comb] = new List<int>(dp[prev]);
 dp[comb].Add(i);
 }
 }
 }
 return dp[(1 << n) - 1].ToArray();
 }
 }
 
 | 
 [sol1-Python3]| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | class Solution:def smallestSufficientTeam(self, req_skills: List[str], people: List[List[str]]) -> List[int]:
 n, m = len(req_skills), len(people)
 skill_index = {v: i for i, v in enumerate(req_skills)}
 dp = [None] * (1 << n)
 dp[0] = []
 for i, p in enumerate(people):
 cur_skill = 0
 for s in p:
 cur_skill |= 1 << skill_index[s]
 for prev in range(1 << n):
 if dp[prev] == None:
 continue
 comb = prev | cur_skill
 if dp[comb] == None or len(dp[comb]) > len(dp[prev]) + 1:
 dp[comb] = dp[prev] + [i]
 return dp[(1 << n) - 1]
 
 | 
 [sol1-C]| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 
 | typedef struct {char *key;
 int val;
 UT_hash_handle hh;
 } HashItem;
 
 HashItem *hashFindItem(HashItem **obj, char *key) {
 HashItem *pEntry = NULL;
 HASH_FIND_STR(*obj, key, pEntry);
 return pEntry;
 }
 
 bool hashAddItem(HashItem **obj, char *key, int val) {
 if (hashFindItem(obj, key)) {
 return false;
 }
 HashItem *pEntry = (HashItem *)malloc(sizeof(HashItem));
 pEntry->key = key;
 pEntry->val = val;
 HASH_ADD_STR(*obj, key, pEntry);
 return true;
 }
 
 bool hashSetItem(HashItem **obj, char *key, int val) {
 HashItem *pEntry = hashFindItem(obj, key);
 if (!pEntry) {
 hashAddItem(obj, key, val);
 } else {
 pEntry->val = val;
 }
 return true;
 }
 
 int hashGetItem(HashItem **obj, char *key, int defaultVal) {
 HashItem *pEntry = hashFindItem(obj, key);
 if (!pEntry) {
 return defaultVal;
 }
 return pEntry->val;
 }
 
 void hashFree(HashItem **obj) {
 HashItem *curr = NULL, *tmp = NULL;
 HASH_ITER(hh, *obj, curr, tmp) {
 HASH_DEL(*obj, curr);
 free(curr);
 }
 }
 
 int* smallestSufficientTeam(char ** req_skills, int req_skillsSize, char *** people, int peopleSize, int* peopleColSize, int* returnSize) {
 int n = req_skillsSize, m = peopleSize;
 HashItem *skill_index = NULL;
 for (int i = 0; i < n; ++i) {
 hashAddItem(&skill_index, req_skills[i], i);
 }
 
 int *dp[1 << n], dpColSize[1 << n];
 memset(dpColSize, 0, sizeof(dpColSize));
 for (int i = 0; i < (1 << n); i++) {
 dp[i] = NULL;
 }
 dp[0] = (int *)calloc(m, sizeof(int));
 for (int i = 0; i < m; ++i) {
 int cur_skill = 0;
 for (int j = 0; j < peopleColSize[i]; j++) {
 cur_skill |= 1 << hashGetItem(&skill_index, people[i][j], 0);
 }
 for (int prev = 0; prev < (1 << n); ++prev) {
 if (dp[prev] == NULL) {
 continue;
 }
 int comb = prev | cur_skill;
 if (dp[comb] == NULL || dpColSize[prev] + 1 < dpColSize[comb]) {
 dp[comb] = (int *)calloc(m, sizeof(int));
 memcpy(dp[comb], dp[prev], sizeof(int) * dpColSize[prev]);
 dpColSize[comb] = dpColSize[prev];
 dp[comb][dpColSize[comb]++] = i;
 }
 }
 }
 for (int i = 0; i < (1 << n) - 1; i++) {
 if (dp[i]) {
 free(dp[i]);
 }
 }
 *returnSize = dpColSize[(1 << n) - 1];
 return dp[(1 << n) - 1];
 }
 
 | 
 [sol1-Go]| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 
 | func smallestSufficientTeam(req_skills []string, people [][]string) []int {n, m := len(req_skills), len(people)
 skill_index := make(map[string]int)
 for i, skill := range req_skills {
 skill_index[skill] = i
 }
 dp := make([][]int, 1 << n)
 dp[0] = []int {}
 for i := 0; i < m; i++ {
 cur_skill := 0
 for _, s := range people[i] {
 cur_skill |= 1 << skill_index[s]
 }
 for prev := 0; prev < len(dp); prev++ {
 if dp[prev] == nil {
 continue
 }
 comb := prev | cur_skill
 if dp[comb] == nil || len(dp[prev]) + 1 < len(dp[comb]) {
 dp[comb] = make([]int, len(dp[prev]))
 copy(dp[comb], dp[prev])
 dp[comb] = append(dp[comb], i)
 }
 }
 }
 return dp[(1 << n) - 1]
 }
 
 | 
 [sol1-JavaScript]| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 
 | var smallestSufficientTeam = function(req_skills, people) {const n = req_skills.length;
 const m = people.length;
 let skillIndex = new Map();
 for (let i = 0; i < n; i++) {
 skillIndex.set(req_skills[i], i);
 }
 let dp = new Array(1 << n);
 dp[0] = [];
 for (let i = 0; i < m; i++) {
 let cur_skill = 0;
 for (let s of people[i]) {
 cur_skill |= 1 << skillIndex.get(s);
 }
 for (let prev = 0; prev < dp.length; prev++) {
 if (dp[prev] === undefined) {
 continue;
 }
 let comb = prev | cur_skill;
 if (dp[comb] === undefined || dp[prev].length + 1 < dp[comb].length) {
 dp[comb] = [...dp[prev], i];
 }
 }
 }
 return dp[(1 << n) - 1];
 };
 
 | 
 复杂度分析
- 时间复杂度:O(m^{2} \times 2^{n}),其中 n 是 req_skills 的长度,m 是 peoples 的长度。
- 空间复杂度:O(m \times 2^{n}),其中 n 是 req_skills 的长度,m 是 peoples 的长度。
方法二:动态规划 + 优化
思路与算法
在方法一中,我们用 dp}[i] 来表示状态,状态含义是满足技能集合为 i 的最小人数的数组,每一个状态都用数组记录了具体的人员编号。这个过程浪费很多空间去储存结果,也消耗了很多时间去生成数组。
实际上我们只去要记录下每个状态的产生来源,就可以按序还原每个状态的具体人员编号的数组。
我们用:
- dp}[i] 来表示,满足技能集合为 i 的最小人数。类似方法一中,我们初始化 dp}[0] = 0,其它 dp}[i] 初始为最大值 m。
- prev_skill}[i] 来表示先前的技能集合,技能集合 i 是从 prev_skill}[i] 转移来的。
- prev_people}[i] 来表示一个最新加入的员工,技能集合 i 是通过加入员工 prev_people}[i] 而转移来的。
通过这样方式,我们就记录了每一个状态的转移来源。
最后,所有技能的集合用 (1 << n) - 1 来表示,其中 n 是 req_skills 的长度。当我们要复原一个技能集合 i 的时候,我们可以找到最后一个员工 prev_people}[i], 把它加入结果中,然后赋值 i 为 prev_skill}[i]。不断重复这个过程,直到 i = 0,表示我们已找到需要技能集合的最少员工。
代码
[sol2-Java]| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 
 | class Solution {public int[] smallestSufficientTeam(String[] req_skills, List<List<String>> people) {
 int n = req_skills.length, m = people.size();
 HashMap<String, Integer> skill_index = new HashMap<>();
 for (int i = 0; i < n; ++i) {
 skill_index.put(req_skills[i], i);
 }
 int[] dp = new int[1 << n];
 Arrays.fill(dp, m);
 dp[0] = 0;
 int[] prev_skill = new int[1 << n];
 int[] prev_people = new int[1 << n];
 for (int i = 0; i < m; i++) {
 List<String> p = people.get(i);
 int cur_skill = 0;
 for (String s : p) {
 cur_skill |= 1 << skill_index.get(s);
 }
 for (int prev = 0; prev < (1 << n); prev++) {
 int comb = prev | cur_skill;
 if (dp[comb] > dp[prev] + 1) {
 dp[comb] = dp[prev] + 1;
 prev_skill[comb] = prev;
 prev_people[comb] = i;
 }
 }
 }
 List<Integer> res = new ArrayList<>();
 int i = (1 << n) - 1;
 while (i > 0) {
 res.add(prev_people[i]);
 i = prev_skill[i];
 }
 return res.stream().mapToInt(j -> j).toArray();
 }
 }
 
 | 
 [sol2-C++]| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 
 | class Solution {public:
 vector<int> smallestSufficientTeam(vector<string>& req_skills, vector<vector<string>>& people) {
 int n = req_skills.size(), m = people.size();
 unordered_map<string, int> skill_index;
 for (int i = 0; i < n; ++i) {
 skill_index[req_skills[i]] = i;
 }
 vector<int> dp(1 << n, m);
 dp[0] = 0;
 vector<int> prev_skill(1 << n, 0);
 vector<int> prev_people(1 << n, 0);
 for (int i = 0; i < m; ++i) {
 int cur_skill = 0;
 for (string& skill : people[i]) {
 cur_skill |= 1 << skill_index[skill];
 }
 for (int prev = 0; prev < (1 << n); prev++) {
 int comb = prev | cur_skill;
 if (dp[comb] > dp[prev] + 1) {
 dp[comb] = dp[prev] + 1;
 prev_skill[comb] = prev;
 prev_people[comb] = i;
 }
 }
 }
 vector<int> res;
 int i = (1 << n) - 1;
 while (i > 0) {
 res.push_back(prev_people[i]);
 i = prev_skill[i];
 }
 return res;
 }
 };
 
 
 | 
 [sol2-C#]| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 
 | public class Solution {public int[] SmallestSufficientTeam(string[] req_skills, IList<IList<string>> people) {
 int n = req_skills.Length, m = people.Count;
 IDictionary<string, int> skill_index = new Dictionary<string, int>();
 for (int i = 0; i < n; ++i) {
 skill_index.Add(req_skills[i], i);
 }
 int[] dp = new int[1 << n];
 for (int i = 0; i < dp.Length; i++) {
 dp[i] = m;
 }
 dp[0] = 0;
 int[] prev_skill = new int[1 << n];
 int[] prev_people = new int[1 << n];
 for (int i = 0; i < m; ++i) {
 int cur_skill = 0;
 foreach (string s in people[i]) {
 cur_skill |= 1 << skill_index[s];
 }
 for (int prev = 0; prev < dp.Length; prev++) {
 int comb = prev | cur_skill;
 if (dp[comb] > dp[prev] + 1) {
 dp[comb] = dp[prev] + 1;
 prev_skill[comb] = prev;
 prev_people[comb] = i;
 }
 }
 }
 List<int> res = new List<int>();
 int skills = (1 << n) - 1;
 while (skills > 0) {
 res.Add(prev_people[skills]);
 skills = prev_skill[skills];
 }
 return res.ToArray();
 }
 }
 
 | 
 [sol2-Python3]| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 
 | class Solution:def smallestSufficientTeam(self, req_skills: List[str], people: List[List[str]]) -> List[int]:
 n, m = len(req_skills), len(people)
 skill_index = {v: i for i, v in enumerate(req_skills)}
 dp = [m] * (1 << n)
 dp[0] = 0
 prev_skill = [0] * (1 << n)
 prev_people = [0] * (1 << n)
 for i, p in enumerate(people):
 cur_skill = 0
 for s in p:
 cur_skill |= 1 << skill_index[s]
 for prev in range(1 << n):
 comb = prev | cur_skill
 if dp[comb] > dp[prev] + 1:
 dp[comb] = dp[prev] + 1
 prev_skill[comb] = prev
 prev_people[comb] = i
 res = []
 i = (1 << n) - 1
 while i > 0:
 res.append(prev_people[i])
 i = prev_skill[i]
 return res
 
 | 
 [sol2-Go]| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 
 | func smallestSufficientTeam(req_skills []string, people [][]string) []int {n, m := len(req_skills), len(people)
 skill_index := make(map[string]int)
 for i, skill := range req_skills {
 skill_index[skill] = i
 }
 dp := make([]int, 1 << n)
 for i := range dp {
 dp[i] = m
 }
 dp[0] = 0
 prev_skill := make([]int, 1 << n)
 prev_people := make([]int, 1 << n)
 for i := 0; i < m; i++ {
 cur_skill := 0
 for _, s := range people[i] {
 cur_skill |= 1 << skill_index[s]
 }
 for prev := 0; prev < (1 << n); prev++ {
 comb := prev | cur_skill
 if dp[comb] > dp[prev]+1 {
 dp[comb] = dp[prev] + 1
 prev_skill[comb] = prev
 prev_people[comb] = i
 }
 }
 }
 res := []int{}
 i := (1 << n) - 1
 for i > 0 {
 res = append(res, prev_people[i])
 i = prev_skill[i]
 }
 return res
 }
 
 | 
 [sol2-JavaScript]| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 
 | var smallestSufficientTeam = function(req_skills, people) {const n = req_skills.length;
 const m = people.length;
 let skillIndex = new Map();
 for (let i = 0; i < n; i++) {
 skillIndex.set(req_skills[i], i);
 }
 let dp = new Array(1 << n).fill(m);
 dp[0] = 0;
 let prev_skill = new Array(1 << n);
 let prev_people = new Array(1 << n);
 for (let i = 0; i < m; i++) {
 let cur_skill = 0;
 for (let s of people[i]) {
 cur_skill |= 1 << skillIndex.get(s);
 }
 for (let prev = 0; prev < (1 << n); prev++) {
 let comb = prev | cur_skill;
 if (dp[comb] > dp[prev] + 1) {
 dp[comb] = dp[prev] + 1;
 prev_skill[comb] = prev;
 prev_people[comb] = i;
 }
 }
 }
 let res = [];
 let skills = (1 << n) - 1;
 while (skills > 0) {
 res.push(prev_people[skills]);
 skills = prev_skill[skills];
 }
 return res;
 };
 
 | 
 [sol2-C]| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 
 | typedef struct {char *key;
 int val;
 UT_hash_handle hh;
 } HashItem;
 
 HashItem *hashFindItem(HashItem **obj, char *key) {
 HashItem *pEntry = NULL;
 HASH_FIND_STR(*obj, key, pEntry);
 return pEntry;
 }
 
 bool hashAddItem(HashItem **obj, char *key, int val) {
 if (hashFindItem(obj, key)) {
 return false;
 }
 HashItem *pEntry = (HashItem *)malloc(sizeof(HashItem));
 pEntry->key = key;
 pEntry->val = val;
 HASH_ADD_STR(*obj, key, pEntry);
 return true;
 }
 
 bool hashSetItem(HashItem **obj, char *key, int val) {
 HashItem *pEntry = hashFindItem(obj, key);
 if (!pEntry) {
 hashAddItem(obj, key, val);
 } else {
 pEntry->val = val;
 }
 return true;
 }
 
 int hashGetItem(HashItem **obj, char *key, int defaultVal) {
 HashItem *pEntry = hashFindItem(obj, key);
 if (!pEntry) {
 return defaultVal;
 }
 return pEntry->val;
 }
 
 void hashFree(HashItem **obj) {
 HashItem *curr = NULL, *tmp = NULL;
 HASH_ITER(hh, *obj, curr, tmp) {
 HASH_DEL(*obj, curr);
 free(curr);
 }
 }
 
 int* smallestSufficientTeam(char ** req_skills, int req_skillsSize, char *** people, int peopleSize, int* peopleColSize, int* returnSize) {
 int n = req_skillsSize, m = peopleSize;
 HashItem *skill_index = NULL;
 for (int i = 0; i < n; ++i) {
 hashAddItem(&skill_index, req_skills[i], i);
 }
 
 int dp[1 << n];
 int prev_skill[1 << n], prev_people[1 << n];
 memset(prev_skill, 0, sizeof(prev_skill));
 memset(prev_people, 0, sizeof(prev_people));
 dp[0] = 0;
 for (int i = 1; i < (1 << n); i++) {
 dp[i] = m;
 }
 for (int i = 0; i < m; ++i) {
 int cur_skill = 0;
 for (int j = 0; j < peopleColSize[i]; j++) {
 cur_skill |= 1 << hashGetItem(&skill_index, people[i][j], 0);
 }
 for (int prev = 0; prev < (1 << n); prev++) {
 int comb = prev | cur_skill;
 if (dp[comb] > dp[prev] + 1) {
 dp[comb] = dp[prev] + 1;
 prev_skill[comb] = prev;
 prev_people[comb] = i;
 }
 }
 }
 
 hashFree(&skill_index);
 int *res = (int *)calloc(m, sizeof(int));
 int i = (1 << n) - 1;
 int pos = 0;
 while (i > 0) {
 res[pos++] = prev_people[i];
 i = prev_skill[i];
 }
 *returnSize = pos;
 return res;
 }
 
 | 
 复杂度分析
- 时间复杂度:O(m \times 2^{n}),其中 n 是 req_skills 的长度,m 是 peoples 的长度。
- 空间复杂度:O(2^{n}),其中 n 是 req_skills 的长度。