【每日一题Day171】LC1125最小的必要团队 | 01背包 位运算

文章介绍了如何使用01背包的动态规划策略解决找到拥有所有需求技能的最小团队成员数量的问题。通过状态压缩表示员工技能和需求,建立一维动态规划数组,然后遍历员工和技能需求,更新最小团队规模。最后,通过计算二进制中1的个数最少的结果来确定团队成员。时间复杂度为O(T+m2^n),空间复杂度为O(S+n2m)。
摘要由CSDN通过智能技术生成
最小的必要团队【LC1125】

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

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

  • 例如,团队 team = [0, 1, 3] 表示掌握技能分别为 people[0]people[1],和 people[3] 的备选人员。

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

知道是01背包 状态压缩 但是代码写不出来
最近的每日一题还都挺复杂

  • 思路

    每位备选人员有选或者不选两种可能,而我们最终的目标是使找到具有所有技能的最少员工,该问题实质上为01背包问题。

    • 物品为每个员工的技能,背包容量为所有需要的技能。【用状态压缩表示技能】

    • 定义状态 d p [ j ] dp[j] dp[j]表示 所有员工技能为 j j j时需要的最少员工【用状态压缩表示员工】

  • 一维动态规划

    1. 确定dp数组(dp table)以及下标的含义

      d p [ j ] dp[j] dp[j]表示 所有员工技能为 j j j时需要的最少员工

    2. 确定递推公式

      对于每一个员工,若其具有的技能为mask,那么对于背包 j j j而言

      • 不选员工 i i i d p [ j ] = d p [ j ] dp[j] = dp[j] dp[j]=dp[j]

      • 选员工 i i i

        除了员工 i i i所具有的技能,背包 j j j还需要的技能为mask & ~j
        d p [ j ] = d p [ m a s k & ! j ] ∣ ( 1 < < i ) dp[j]=dp[mask\& !{j}] | (1 << i) dp[j]=dp[mask&!j](1<<i)

      取员工数目最少的结果,即二进制中1的个数最少的结果

    3. dp数组如何初始化

      最差情况时所有员工都取,因此 d p [ 1 : ] dp[1:] dp[1:]初始化为(1>>m - 1), d p [ 0 ] dp[0] dp[0]初始化为0

    4. 确定遍历顺序

      一维dp

      先遍历物品,再从后往前遍历背包重量

    5. 举例推导dp数组

    class Solution {
        public int[] smallestSufficientTeam(String[] reqSkills, List<List<String>> people) {
            var sid = new HashMap<String, Integer>();
            int m = reqSkills.length;
            for (int i = 0; i < m; ++i)
                sid.put(reqSkills[i], i); // 字符串映射到下标
    
            int n = people.size(), u = 1 << m;
            // f[i+1][j] 表示从前 i 个集合中选择一些集合,并集等于 j,需要选择的最小集合
            var f = new long[n + 1][u];
            Arrays.fill(f[0], (1L << n) - 1); // 对应记忆化搜索中的 if (i < 0) return all;
            f[0][0] = 0;
            for (int i = 0; i < n; ++i) {
                int mask = 0;
                for (var s : people.get(i)) // 把 people[i] 压缩成一个二进制数 mask
                    mask |= 1 << sid.get(s);
                for (int j = 1; j < u; ++j) {
                    long res = f[i][j]; // 不选 mask
                    long res2 = f[i][j & ~mask] | (1L << i); // 选 mask
                    f[i + 1][j] = Long.bitCount(res) < Long.bitCount(res2) ? res : res2;
                }
            }
    
            long res = f[n][u - 1];
            var ans = new int[Long.bitCount(res)];
            for (int i = 0, j = 0; i < n; ++i)
                if (((res >> i) & 1) > 0)
                    ans[j++] = i; // 所有在 res 中的下标
            return ans;
        }
    }
    
    作者:灵茶山艾府
    链接:https://leetcode.cn/problems/smallest-sufficient-team/solutions/2214387/zhuang-ya-0-1-bei-bao-cha-biao-fa-vs-shu-qode/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    • 复杂度

      • 时间复杂度: O ( T + m 2 n ) O(T+m2^n) O(T+m2n),n为reqSkills长度,m为peopel的长度,T为people中字符串长度之和

      • 空间复杂度: O ( S + n 2 m ) O(S+n2^m) O(S+n2m),S为reqSkills中字符串长度之和

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值