[LeetCode] 1125. The minimum necessary team (hard) - state pressure dp+backtracking

[topic link]

https://leetcode.cn/problems/smallest-sufficient-team/

【The main idea of ​​the topic】

Given a list of required skills and a set of people with those skills. Finds a minimum number of teams such that the skills in the team cover the given list of skills. Guarantee full coverage, and everyone's skills are the required skills. So in fact, it is to find a minimum number of subsets so that the union is equal to the given set.

【Input example】

Input: req_skills = ["java", "nodejs", "reactjs"], people = [["java"], ["nodejs"], ["nodejs", "reactjs"]] Output: [0,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]

【data range】

1 <= req_skills.length <= 16
1 <= req_skills[i].length <= 16
req_skills[i] consists of lowercase English letters
All strings in req_skills are different from each other
1 <= people.length <= 60
0 < = people[i].length <= 16
1 <= people[i][j].length <= 16
people[i][j] consists of lowercase English letters
all strings in people[i] are different
people Each skill in [i] is a skill in req_skills
The title data guarantees that the "necessary team" must exist


pressure dp

  看到 r e q _ s k i l l s . l e n g t h < = 16 req\_skills.length <= 16 req_skills.length<=16 , the first thing to think about is state compression. Find the union of some subsets to get a large set, corresponding to the OR operation. assumereq_skills req\_skillsreq_skills p e o p l e people The lengths of p eo ple e aremmm andnnn , we can use an arraydp [ 1 < < m ] dp[1<<m]dp[1<<m ] to represent each subset (that is, state),dp [ i ] dp[i]d p ​​[ i ] is a set that represents the smallest team member number that constitutes the current state. Since the number of skills for each person is at most 16, we can also encode each person's skill set into a decimal number, expressed asskill_set skill\_sets kill _ se t . _
  First you need to traverse all states (usingiii means) yes. For each state, we iterate over the list of persons (usingjjj represents), for the current state, after adding the current employee, the employee’s skills will make the skill set enter a new statenew _ stats = i ∣ skill _ set [ j ] new\_stats=i|skill\_set[j]new_stats=iskill_set[j],如果 d p [ i ] . s i z e + 1 < d p [ n e w _ s t a t s ] . s i z e dp[i].size+1<dp[new\_stats].size dp[i].size+1<d p ​​[ n e w _ s t a t s ] . s i ze , that is, the addition of the current set makes the new state have a new and smaller solution, we update this minimum solution, and list the current state personnel Add the current personnel and hang into the new state. Wheni = ( 1 < < m ) − 1 i=(1<<m)-1i=(1<<m)1 , that is, all binary bits are 1, at this timedp [ ( 1 < < m ) − 1 ] dp[(1<<m)-1]dp[(1<<m)1 ] The set represented by is the minimum team required for the answer.

class Solution {
    
    
public:
    vector<int> smallestSufficientTeam(vector<string>& req_skills, vector<vector<string>>& people) {
    
    
        int m=req_skills.size(),n=people.size();
        map<string,int> idx;//每个技能的编码位置
        for(int i=0;i<m;i++) idx[req_skills[i]]=i;
        vector<int> skill_set(n,0);//每个人的技能集合编码
        for(int i=0;i<n;i++){
    
    
            for(auto s:people[i]){
    
    
                skill_set[i]|=1<<idx[s];
            }
        }

        vector<vector<int>> dp(1<<m);//总的技能集合状态
        for(int i=0;i<(1<<m);i++){
    
    
            if(i&&dp[i].empty()) continue;//不是初始状体且集合为空,表示状态不可达,直接跳过
            for(int j=0;j<n;j++){
    
    
                int new_stats=i|skill_set[j];
                if(!new_stats) continue;//空集需要跳过,因为空集满足更新条件,但显然不需要
                if(dp[new_stats].empty()||dp[i].size()+1<dp[new_stats].size()){
    
    //新状态没去过或者是有更优解
                    dp[new_stats]=dp[i];
                    dp[new_stats].push_back(j);
                }
            }
        }
        return dp[(1<<m)-1];
    }
};



Optimizing space with backtracking

  For each state, we use a collection of employee numbers to represent, which leads to a large space overhead. And obviously a lot of data is repeated. For the state transition process of the optimal solution, the set of employee numbers in the previous state must be a subset of the employee numbers in the next state, and the difference is limited to the numbers of the currently joined employees. So can we optimize this repeated space? sure. We can reduce this duplication overhead by using two additional arrays. pre_stats[i] pre\_stats[i]p re _ s t a t s [ i ] is used to represent the previous state of the current state;pre _ people [ i ] pre\_people[i]p re _ p eo pl e [ i ] is used to indicate the cause of transfer toiiThe ID of the newly joined employee in the i state. After running dp, we only need to start frompre_stats [ ( 1 < < m ) − 1 ] pre\_stats[(1<<m)-1]pre_stats[(1<<m)1 ] Start backtracking, and each read causes a transition to this state ofpre_people pre\_peoplep re _ p eo ple adds a new set, and the resulting set is the result set.

class Solution {
    
    
public:
    vector<int> smallestSufficientTeam(vector<string>& req_skills, vector<vector<string>>& people) {
    
    
        int m=req_skills.size(),n=people.size();
        map<string,int> idx;//每个技能的编码位置
        for(int i=0;i<m;i++) idx[req_skills[i]]=i;
        vector<int> skill_set(n,0);//每个人的技能集合编码
        for(int i=0;i<n;i++){
    
    
            for(auto s:people[i]){
    
    
                skill_set[i]|=1<<idx[s];
            }
        }

        vector<int> dp(1<<m,INT_MAX);//总的技能集合状态
        vector<int> pre_people(1<<m);//上一个贡献技能的人
        vector<int> pre_stats(1<<m);//上一个状态
        
        dp[0]=0;
        for(int i=0;i<(1<<m);i++){
    
    
            if(dp[i]==INT_MAX) continue;//状态不可达,直接跳过
            for(int j=0;j<n;j++){
    
    
                int new_stats=i|skill_set[j];
                if(dp[i]+1<dp[new_stats]){
    
    
                    dp[new_stats]=dp[i]+1;
                    pre_stats[new_stats]=i;
                    pre_people[new_stats]=j;
                }
            }
        }
        vector<int> res;
        for(int i=(1<<m)-1;i;i=pre_stats[i]){
    
    
            res.push_back(pre_people[i]);
        }
        return res;
    }
};

Guess you like

Origin blog.csdn.net/qq_44623371/article/details/130028818