題目描述:
作爲項目經理,你規劃了一份需求的技能清單 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]
思路:
這道題就是求母集合最少可以被多少個子集和組成。這種最優狀態通常可以用動態規劃來解決,但是難點在於你該如何表示團隊已有技能的狀態呢?看了網上的大神的題解,用二進制位來代表技能,0/1代表無/有對應的該項技能,那麼問題就開始明朗起來了,我們可以根據技能清單,利用map來對每個技能編0-(size-1)的號代表技能對應的位,子集i和對應的狀態have[i]就是裏面元素之或,然後定義一個dp數組用於動態規劃,dp[0]=0,狀態轉移方程if(dp[j|have[i]]>dp[j]+1) dp[j|have[i]]=dp[j]+1,如果某個狀態j在加上某個子集have[i]得到的狀態j|have[i]所需的最小子集dp[j|have[i]]大於原狀態所需最小子集dp[j]加一,那麼說明原狀態j加上這個子集have[i]得到的所需最小子集dp[j|have[i]]是更優的。我們在更新狀態的時候同時用一個數組記錄下最優子集的編號。
以下爲代碼:
class Solution {
public:
vector<int> smallestSufficientTeam(vector<string>& req_skills, vector<vector<string>>& people) {
unordered_map<string,int> skills;
vector<int> dp(1<<req_skills.size(),-1);
vector<pair<int,int>> record(1<<req_skills.size(),{-1,-1});
vector<int> ans;
for(int i=0;i<req_skills.size();i++)
skills[req_skills[i]]=i;
dp[0]=0;
for(int i=0;i<people.size();i++)
{
int have=0;
for(string skill:people[i])
have|=1<<skills[skill];
for(int j=0;j<dp.size();j++)
{
if(dp[j]==-1) continue;
if((dp[j|have]>dp[j]+1)||(dp[j|have]==-1))
{
dp[j|have]=dp[j]+1;
record[j|have].first=i;
record[j|have].second=j;
}
}
}
int ansb=(1<<req_skills.size())-1;
while(ansb>0)
{
ans.push_back(record[ansb].first);
ansb=record[ansb].second;
}
return ans;
}
};
用位來代表是否擁有該技能,將其轉化爲一個動態規劃,這方法就是想不到,太久沒做題了,現在實在是太菜了。