一、Problem
作为项目经理,你规划了一份需求的技能清单 req_skills,并打算从备选人员名单 people 中选出些人组成一个「必要团队」( 编号为 i 的备选人员 people[i] 含有一份该备选人员掌握的技能列表)。
所谓「必要团队」,就是在这个团队中,对于所需求的技能列表 req_skills 中列出的每项技能,团队中至少有一名成员已经掌握。
我们可以用每个人的编号来表示团队中的成员:例如,团队 team = [0, 1, 3] 表示掌握技能分别为 people[0],people[1],和 people[3] 的备选人员。
请你返回 任一 规模最小的必要团队,团队成员用人员编号表示。你可以按任意顺序返回答案,本题保证答案存在。
输入:req_skills = ["java","nodejs","reactjs"], people = [["java"],["nodejs"],["nodejs","reactjs"]]
输出:[0,2]
提示:
1 <= req_skills.length <= 16
1 <= people.length <= 60
1 <= people[i].length, req_skills[i].length, people[i][j].length <= 16
req_skills 和 people[i] 中的元素分别各不相同
req_skills[i][j], people[i][j][k] 都由小写英文字母组成
本题保证「必要团队」一定存在
二、Solution
方法一:dp
- 定义状态:
- 表示团队中技能满足状态为 时,所需要的最少人数
- 思考初始化:
- 表示没有任何一项技能满足时的团队需要 0 个人
- 思考状态转移方程:
- 如果 或者 ,则
- 思考输出:输出满足所有技能后的最少人数团队成员编号。
由于字符串难以做成位操作,所以要将字符串和技能编号映射起来。
class Solution {
public int[] smallestSufficientTeam(String[] rss, List<List<String>> pss) {
int n = rss.length, m = pss.size(), tot = (1 << n) - 1, f[] = new int[tot+1];
Arrays.fill(f, -1);
f[0] = 0;
Map<String, Integer> mp = new HashMap<>(n);
for (int i = 0; i < n; i++)
mp.put(rss[i], i);
Map<Integer, List<Integer>> tm = new HashMap<>();
for (int i = 0; i <= tot; i++)
tm.put(i, new LinkedList<>());
for (int i = 0; i < m; i++) {
int x = 0;
for (String sk : pss.get(i))
x = x | 1 << mp.get(sk); //选取第i个人的技能
for (int j = 0; j <= tot; j++) {//团队已存在的技能状态
if (f[j] == -1)
continue;
int y = x | j; //已有的技能+第i个人的技能
if (f[y] == -1 || f[y] > f[j] + 1) {
f[y] = f[j] + 1;
tm.get(y).clear();
tm.get(y).addAll(tm.get(j));
tm.get(y).add(i);
}
}
}
int sz = tm.get(tot).size(), idx = 0, a[] = new int[sz];
for (int pID : tm.get(tot))
a[idx++] = pID;
return a;
}
}
复杂度分析
- 时间复杂度: ,
- 空间复杂度: ,