一、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
思路
需滿足的技能數量很小,暗示我們可以將團隊的技能狀態用一個整形變量來記錄;首先在狀態轉移之前,我們肯定需要得到某一個人的掌握的技能,然後將該人的技能狀態跟已經存在的團隊的技能狀態 j 相融合,設融合後的狀態爲 y:
- 如果狀態 y 之前不存在,那麼團隊技能狀態 y 所需的人數直接加上該人
- 如果狀態 y 之前存在過,那麼團隊技能狀態 y 所需的人數應該與狀態爲 時加上 1 人對比,看誰所需人數少
算法
- 定義狀態:
- 表示團隊中技能滿足狀態爲 時,所需要的最少人數
- 思考初始化:
- 表示沒有任何一項技能滿足時的團隊需要 0 個人
- 思考狀態轉移方程:
- 如果 或者 ,則
- 思考輸出:輸出滿足所有能後的最少人數團隊成員編號。
由於字符串難以做成位操作,所以要將字符串和技能編號映射起來。
class Solution {
public int[] smallestSufficientTeam(String[] rss, List<List<String>> ps) {
int n = rss.length, m = ps.size(), tot = (1 << n) - 1, f[] = new int[tot+1];
Arrays.fill(f, -1);
f[0] = 0;
Map<String, Integer> mp = new HashMap<>();
Map<Integer, List<Integer>> team = new HashMap<>();
for (int i = 0; i < n; i++) mp.put(rss[i], i);
for (int i = 0; i <= tot; i++) team.put(i, new LinkedList<>());
for (int i = 0; i < m; i++) {
int x = 0;
for (String s : ps.get(i))
x |= 1 << mp.get(s);
for (int j = 0; j <= tot; j++) if (f[j] != -1) {
int y = j | x;
if (f[y] == -1 || f[y] > f[j] + 1) {
f[y] = f[j] + 1;
team.get(y).clear();
team.get(y).addAll(team.get(j));
team.get(y).add(i);
}
}
}
int sz = team.get(tot).size(), i = 0, ans[] = new int[sz];
for (int pID : team.get(tot))
ans[i++] = pID;
return ans;
}
}
複雜度分析
- 時間複雜度:,
- 空間複雜度:,