作为项目经理,你规划了一份需求的技能清单 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]
题解:很显然的可重覆盖问题了,舞蹈链直接解即可,关于舞蹈链,详见:
当然,本题就面试而言,还是先答一下DP+二进制记录状态。
struct DLX{
static const int maxn = 111;
static const int maxm = 111;
static const int maxnode = maxn*maxm;
int n,m,size;
int U[maxnode],D[maxnode],R[maxnode],L[maxnode],Row[maxnode],Col[maxnode];
int H[maxn],S[maxm];
int ansd,ans1[111111], ans[111111];
void init(int _n,int _m)
{
n = _n,m = _m;
for(int i = 0;i<=m;++i)
{
S[i] = 0;
U[i] = D[i] = i;
L[i] = i-1;
R[i] = i+1;
}
R[m] = 0,L[0] = m;
size = m;
for(int i = 1;i<=n;++i)H[i] = -1;
}
void link(int r,int c)
{
++S[ Col[++size] = c ];
Row[size] = r;
D[size] = D[c];
U[ D[c] ] = size;
U[size] = c;
D[c] = size;
if(H[r] < 0)H[r] = L[size] = R[size] = size;
else
{
R[size] = R[H[r]];
L[R[H[r]]] = size;
L[size] = H[r];
R[H[r]] = size;
}
}
void remove(int c)
{
for(int i = D[c]; i != c;i = D[i])
L[ R[i] ] = L[i],R[ L[i] ] = R[i];
}
void resume(int c)
{
for(int i = U[c]; i != c;i = U[i])
L[R[i]] = R[ L[i] ] = i;
}
bool v[maxm];
int f()
{
int res = 0;
for(int c = R[0];c;c = R[c])v[c] = true;
for(int c = R[0];c;c=R[c])
if(v[c])
{
res++;
v[c] = false;
for(int i = D[c];i!=c;i = D[i])
for(int j = R[i];j!=i;j = R[j])
v[ Col[j] ] = false;
}
return res;
}
void dance(int d)
{
if(d+f() >= ansd)return;
if(R[0] == 0)
{
if(d < ansd)
{
ansd = d;
for(int i = 0;i<d;++i)
ans[i] = ans1[i];
}
return;
}
int c = R[0];
for(int i = R[0];i;i = R[i])
if(S[i] < S[c])
c = i;
for(int i = D[c];i != c;i = D[i])
{
ans1[d] = Row[i];
remove(i);
for(int j = R[i];j != i;j = R[j])remove(j);
dance(d+1);
for(int j = L[i];j != i; j = L[j])resume(j);
resume(i);
}
}
}g;
class Solution {
public:
vector<int> smallestSufficientTeam(vector<string>& req_skills, vector<vector<string>>& people) {
unordered_map<string,int> mp;
for(int i = 0;i < (int)req_skills.size();++i)
mp[req_skills[i]] = i+1;
int n = people.size(),m = req_skills.size();
g.init(n,m);
for(int x = 0;x<(int)people.size();++x)
{
for(int i = 0;i<(int)people[x].size();++i)
{
int y = mp[people[x][i]];
g.link(x+1,y);
}
}
g.ansd = 11111111;
g.dance(0);
vector<int>ans;
for(int i = 0;i<g.ansd;++i)
{
ans.push_back(g.ans[i]-1);
}
return ans;
}
};
DP+二进制记录状态:
class Solution {
public:
vector<int> smallestSufficientTeam(vector<string>& req_skills, vector<vector<string>>& people) {
int n = req_skills.size();
int full = 1<<n;
unordered_map<string,int> skills;
vector<int> dp(full,-1);
unordered_map<int,vector<int>> team;
/*initial*/
for(int i = 0;i < n; ++i){
skills[req_skills[i]] = i;
}
dp[0] = 0;
team[0] = vector<int>();
for(int i = 0;i < people.size(); ++i){
int idx = 0;
for(auto s : people[i]){
if(skills.count(s)){
idx = idx|(1<<skills[s]);
}
}
for(int j = 0;j < full; ++j){
if(dp[j] >= 0){
int x = j|idx;
if(dp[x] == -1 || dp[x] > dp[j] + 1){
dp[x] = dp[j] + 1;
team[x] = team[j];
team[x].push_back(i);
}
}
}
}
return team[full-1];
}
};