LeetCode:最小的必要团队

题目描述:

作为项目经理,你规划了一份需求的技能清单 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;
    }
};

用位来代表是否拥有该技能,将其转化为一个动态规划,这方法就是想不到,太久没做题了,现在实在是太菜了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章