[LeetCode] 1125. 必要最低限のチーム(ハード) - 状態プレッシャーdp+バックトラッキング

[トピックリンク]

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

【トピックの主な考え方】

必要なスキルのリストとそれらのスキルを持つ人材のセットが与えられます。チーム内のスキルが指定されたスキルのリストをカバーするような最小数のチームを見つけます。完全な補償を保証し、誰もが必要なスキルを習得できます。したがって、実際には、和集合が指定されたセットと等しくなるようにサブセットの最小数を見つけることになります。

【入力例】

入力: req_skills = ["java", "nodejs", "reactjs"]、people = [["java"], ["nodejs"], ["nodejs", "reactjs"]] 出力: [0,2
]

输入:req_skills = ["algorithms","math","java","re​​actjs","csharp","aws"], people = [["algorithms","math","java"],["algorithms" ”,”math”,”reactjs”],[”java”,”csharp”,”aws”],[”reactjs”,”csharp”],[”csharp”,”math”],[”aws”, 「java」]]
出力:[1,2]

【データ範囲】

1 <= req_skills.length <= 16
1 <= req_skills[i].length <= 16
req_skills[i] は小文字の英字で構成されます
req_skills 内のすべての文字列は互いに異なります
1 <= people.length <= 60
0 < = people[i].length <= 16
1 <= people[i][j].length <= 16
people[i][j] は英小文字で構成される
people[i] 内のすべての文字列は異なる
people それぞれのスキル[i]はreq_skillsのスキルです
タイトルデータは「必要なチーム」が存在することを保証します


圧力 dp

req_skill を  参照してください。長さ < = 16 req\_skills.length <= 16スキルスキル要求ます_ _ _ _ _<=図16において、最初に考えるべきことは状態圧縮である。いくつかのサブセットの和集合を見つけて、OR 演算に対応する大き​​なセットを取得します。req_skills が req\_skills であると仮定しますre q _ s kill s和人_ _身長mmですmnnnの場合、配列dp [ 1 < < m ] dp[1<< m]d p [ 1<<m ]は各サブセット (つまり、状態) を表します。dp [ i ] dp[i]d p [ i ]は、現在の状態を構成する最小のチームメンバー番号を表す集合です。各個人のスキルの数は最大 16 であるため、各個人のスキル セットを 10 進数にエンコードして、 skill_set skill\_setとして表すこともできます。s kill _ set . _ _
  まず、すべての状態を横断する必要があります (iiつまり)はい。州ごとに、人物のリストを繰り返し処理します (jjjは表します)、現在の状態では、現在の従業員を追加した後、従業員のスキルによってスキル セットが新しい状態に入りますnew _ stats = i ∣ skill _ set [ j ] new\_stats=i|skill\_set[j ]新しい統計_ _ _ _ _ _ _=i skill_set [ j ] 場合はdp [ i ] サイズ + 1 < dp [new_stats] 。サイズ dp[i].size+1<dp[new\_stats].sized p [ i ] そうですね_ _+1<d p [ new w _ s t a t s ] .サイズつまり、現在のセットの追加により新しい状態に新しくて小さな解が追加され、この最小解を更新し、現在のセットをリストします。州職員 現在の職員を追加し、新しい州にぶら下がります。i = ( 1 < < m ) − 1の場合、i=(1<< m)-1=( 1<<メートル1、つまりすべての 2 進ビットが 1、このときdp [( 1 < < m ) − 1 ] dp[(1<< m)-1]d p [( 1<<メートル1 ]で表される集合は、解答に必要な最小チームです。

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];
    }
};



バックトラッキングによるスペースの最適化

  州ごとに従業員番号のコレクションを使用して表すため、スペースのオーバーヘッドが大きくなります。最適解の状態遷移プロセスでは、前の状態の従業員数の集合は次の状態の従業員数の部分集合でなければならず、その差は次の状態の従業員数の部分集合である必要があります。現在入社している社員。では、この繰り返しの空間を最適化することはできるのでしょうか? もちろん。2 つの追加アレイを使用することで、この重複オーバーヘッドを軽減できます。pre_stats[i] pre\_stats[i]pre _ s ta t s [ i ] は、現在の状態の前の状態を表すために使用されます。 pre _ people [ i ] pre\_people[i]pre_people [ i ] はiiの転送の原因を示すために使用ますi状態で新しく参加した従業員の IDdp を実行した後は、 pre_stats [ ( 1 < < m ) − 1 ] pre\_stats[(1<< m)-1]から開始するだけです。事前統計[ ( 1 _ _ _ _ _<<メートル1 ]バックトラックを開始すると、読み取りごとにこのpre_people pre\_peopleの状態に遷移します。pre _ people新しいセットを追加し、その結果のセットが結果セットになります。

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;
    }
};

おすすめ

転載: blog.csdn.net/qq_44623371/article/details/130028818