leetcode5130 最小的必要团队

题目大意:收集最少的人,让这些人能够覆盖 所有的技能。

思路:状态压缩 + 动态规划dp (状态压缩只是一个概念,就是考虑如何用1个数字去表示一个状态,方便我们将这个数字作为dp数组的下标。)

举例:比如说 全部技能有:{'java','csharp'} ,对应数组的下标是{0,1} ;

1. 先说为什么状态压缩,如何状态压缩:

因为题目的技能数目<=16,这很重要,要对数值大小很敏感,因为(1<<16)=1024*64≈70W ,完全可以用一个int容纳。故考虑用2进制表示收集到的技能的状态。

举例:根据上面的例子,那么只收集到{'java'}的状态是 (1<<0) = 1 ; 只收集到 {'csharp'}的状态是 (1<<1) = 2; 收集到 {'java','csharp'}的状态是(1<<0) + (1<<1) = 3 ;

所以收集什么技能,收集到哪一类技能,我们都可以用一个数字去表示。

只收集到{'java'}的状态是1,只收集到{'csharp'}的状态是2,收集到{'java','csharp'}的状态是 1+2 = 3;

总结:

目前我们收集技能的状态,我们可以用一个数字代表这个状态。

同理,每个人掌握技能的情况 也可以用一个数字表示。

2.状态转移

假设总人数是people_num,总技能数是 skill_num;

状态:dp[state]  表示掌握技能的状态为state时,需要的最少人数。根据上面的举例:dp[0] =1 ; dp[1] = 1; dp[3] = 2;

           list[state] 表示这个状态下 人员的编号。

转移方程: dp[nextState] =  min(dp[nextState],dp[ preState | p[i] ] + 1 ) ; (p[i] 是第i个人掌握技能的状态,preState | p[i] == dp[nextState] ) ;

结果要覆盖所有的技能,那么我们要求的状态就是 (1<<(skill_num)-1) ,所以最少的人数就是dp[1<<(skill_num) -1];

结果就是list[ ( 1<

3. 代码:

class Solution {
        public int[] smallestSufficientTeam(String[] req_skills, List> people) {
            int plen = people.size(); //人数
            int slen = req_skills.length; //总技能数
            int dp[] = new int [1<             List rc[] = new ArrayList[1<             
            Map map = new HashMap<>();
            for(int i=0;i                 map.put(req_skills[i], (1<             }
            
            for(int i=0;i<(1<                 rc[i] = new ArrayList<>();
                dp[i] = -1;
            }
            //当状态为0时,也就是没有掌握技能时,需要的人数是0 
            dp[0] = 0;
            
            for(int i=0;i                 int sk = 0;
                for(String skill : people.get(i)){

                   //sk |= map.get(skill) ; //得到这个人掌握技能的状态
                    sk |= ( map.get(skill) == null ? 0 : map.get(skill) );  

      //题目没有明确说"people掌握的技能一定存在于req_skills中",所以判断一下空值比较稳妥, 谢谢评论[快乐崇拜234]指正
                }
                for(int st=0;st<(1<                     if(dp[st]==-1) continue; 
                    int newState = sk|st; 
                    if(dp[newState]==-1 || dp[st]+1                         dp[newState] = dp[st] + 1; //更新人员数量
                        rc[newState].clear();
                        rc[newState].addAll(rc[st]);  //更新人员的编号
                        rc[newState].add(i);
                    }
                }
            }
            
            List rsList = rc[(1<             int rsSz = rsList.size();
            int rsArr [] = new int[rsSz];
            for(int i=0;i                 rsArr[i] = rsList.get(i);
            }
            return rsArr;
        }
    }//如果我还没有讲明白,我很抱歉

4. 反思:一开始我把题目想成了优先队列和并查集,绕了很久没做出来,

还是要对题目数据范围保持敏感,渣科练题少了要多加油了。

你可能感兴趣的:(算法_动态规划)