Leetcode 1125:最小的必要团队

作为项目经理,你规划了一份需求的技能清单 req_skills,并打算从备选人员名单 people 中选出些人组成一个「必要团队」( 编号为 i 的备选人员 people[i] 含有一份该备选人员掌握的技能列表)。

所谓「必要团队」,就是在这个团队中,对于所需求的技能列表 req_skills 中列出的每项技能,团队中至少有一名成员已经掌握。

我们可以用每个人的编号来表示团队中的成员:例如,团队 team = [0, 1, 3] 表示掌握技能分别为 people[0],people[1],和 people[3] 的备选人员。

请你返回 任一 规模最小的必要团队,团队成员用人员编号表示。你可以按任意顺序返回答案,本题保证答案存在。

输入:req_skills = ["java","nodejs","reactjs"], people = [["java"],["nodejs"],["nodejs","reactjs"]]
输出:[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]

思路:dp加二进制表示技能种类。
1、二进制表示技能:题目的技能数目<=16,完全可以用1,表示第一种技能,10表示第二种技能,100表示第三种技能,,以此类推 int完全可以容纳。进而我们可以用一个数字来表示当前拥有的技能总数,像5=4+1,就是拥有了技能1和技能3。1<<skNum -1就是表示拥有了所有的技能。以此来创建动态规划的数组。
2、动态规划:我们遍历每个人的技能,根据每个人的技能去更新dp的值。
状态: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<<skill_num ) - 1] ,人员编号的状态 随着dp的转移而转移。
代码如下:

public int[] smallestSufficientTeam(String[] req_skills, List<List<String>> people) {
            int pNum = people.size();
            int skNum = req_skills.length;
            int[] dp = new int[1<<skNum];
            List<Integer>[] res = new ArrayList[1<<skNum];
            Map<String,Integer> map = new HashMap<>();
            for(int i=0;i<skNum;i++){
                map.put(req_skills[i],1<<i);
            }
            for(int i=0;i<(1<<skNum);i++){
                dp[i]=-1;
                res[i] = new ArrayList<>();
            }
            dp[0]=0;
            for(int i=0;i<pNum;i++){
                int sk=0;
                for(String str:people.get(i))
                    sk|=( map.get(str) == null ? 0 : map.get(str) );//获取当前人的技能类
                for(int st=0;st<(1<<skNum);st++){
                    if(dp[st]==-1) continue;
                    int newState = sk|st;
                    if(dp[newState]==-1||dp[st]+1<dp[newState]){
                        dp[newState] = dp[st] + 1; //更新人员数量
                        res[newState].clear();
                        res[newState].addAll(res[st]);  //更新人员的编号
                        res[newState].add(i);
                    }
                }
            }
            int[] result = new int[res[(1<<skNum)-1].size()];
            for(int i=0;i<result.length;i++){
                result[i] = res[(1<<skNum)-1].get(i);
            }
            return result;
        }

运行结果如下:
在这里插入图片描述希望可以帮到大家,有不懂的地方可以提问。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值